Make installed banger self-contained
Fix the misleading make install path where banger and bangerd still depended on a repo checkout for Firecracker, guest artifacts, image builds, and the SSH key. Replace repo-root inference with an explicit runtime bundle model: resolve a runtime_dir from env/config/install layout, derive concrete artifact paths from it, and update the daemon, CLI, and image-build flow to use those paths. Keep repo_root only as an explicit compatibility alias instead of auto-detecting it. Teach customize.sh to run from a read-only bundled runtime tree while writing transient state under XDG/BANGER_STATE_DIR, and make make install copy the runtime assets into PREFIX/lib/banger so installed binaries stay usable outside the repo. Validate with go test ./..., make build, bash -n customize.sh, and make install DESTDIR=/tmp/banger-install PREFIX=/usr. An out-of-repo installed-binary smoke test was attempted, but this sandbox blocked bangerd from binding its Unix socket (setsockopt: operation not permitted).
This commit is contained in:
parent
375900cf65
commit
ce1be52047
13 changed files with 437 additions and 107 deletions
25
Makefile
25
Makefile
|
|
@ -5,9 +5,15 @@ GOFMT ?= gofmt
|
||||||
INSTALL ?= install
|
INSTALL ?= install
|
||||||
PREFIX ?= $(HOME)/.local
|
PREFIX ?= $(HOME)/.local
|
||||||
BINDIR ?= $(PREFIX)/bin
|
BINDIR ?= $(PREFIX)/bin
|
||||||
|
LIBDIR ?= $(PREFIX)/lib
|
||||||
|
RUNTIMEDIR ?= $(LIBDIR)/banger
|
||||||
DESTDIR ?=
|
DESTDIR ?=
|
||||||
BINARIES := banger bangerd
|
BINARIES := banger bangerd
|
||||||
GO_SOURCES := $(shell find cmd internal -type f -name '*.go' | sort)
|
GO_SOURCES := $(shell find cmd internal -type f -name '*.go' | sort)
|
||||||
|
RUNTIME_EXECUTABLES := firecracker customize.sh dns.sh packages.sh nat.sh namegen
|
||||||
|
RUNTIME_DATA_FILES := packages.apt $(wildcard rootfs.ext4) $(wildcard rootfs-docker.ext4)
|
||||||
|
RUNTIME_BOOT_FILES := wtf/root/boot/vmlinux-6.8.0-94-generic wtf/root/boot/initrd.img-6.8.0-94-generic
|
||||||
|
RUNTIME_MODULES_DIR := wtf/root/lib/modules/6.8.0-94-generic
|
||||||
|
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
|
@ -17,12 +23,12 @@ help:
|
||||||
@printf '%s\n' \
|
@printf '%s\n' \
|
||||||
'Targets:' \
|
'Targets:' \
|
||||||
' make build Build ./banger and ./bangerd' \
|
' make build Build ./banger and ./bangerd' \
|
||||||
' make install Build and install binaries into $(DESTDIR)$(BINDIR)' \
|
' make install Build and install binaries plus the runtime bundle into $(DESTDIR)$(BINDIR) and $(DESTDIR)$(RUNTIMEDIR)' \
|
||||||
' make test Run go test ./...' \
|
' make test Run go test ./...' \
|
||||||
' make fmt Format Go sources under cmd/ and internal/' \
|
' make fmt Format Go sources under cmd/ and internal/' \
|
||||||
' make tidy Run go mod tidy' \
|
' make tidy Run go mod tidy' \
|
||||||
' make clean Remove built Go binaries' \
|
' make clean Remove built Go binaries' \
|
||||||
' make rootfs Run ./make-rootfs.sh'
|
' make rootfs Rebuild the repo-local default rootfs image'
|
||||||
|
|
||||||
build: $(BINARIES)
|
build: $(BINARIES)
|
||||||
|
|
||||||
|
|
@ -45,9 +51,24 @@ clean:
|
||||||
rm -f ./banger ./bangerd
|
rm -f ./banger ./bangerd
|
||||||
|
|
||||||
install: build
|
install: build
|
||||||
|
@for path in $(RUNTIME_EXECUTABLES) $(RUNTIME_BOOT_FILES) $(RUNTIME_MODULES_DIR) packages.apt id_ed25519; do \
|
||||||
|
test -e "$$path" || { echo "missing runtime artifact: $$path" >&2; exit 1; }; \
|
||||||
|
done
|
||||||
|
@test -e rootfs-docker.ext4 || test -e rootfs.ext4 || { echo "missing runtime artifact: rootfs-docker.ext4 or rootfs.ext4" >&2; exit 1; }
|
||||||
mkdir -p "$(DESTDIR)$(BINDIR)"
|
mkdir -p "$(DESTDIR)$(BINDIR)"
|
||||||
|
mkdir -p "$(DESTDIR)$(RUNTIMEDIR)"
|
||||||
|
mkdir -p "$(DESTDIR)$(RUNTIMEDIR)/wtf/root/boot"
|
||||||
|
mkdir -p "$(DESTDIR)$(RUNTIMEDIR)/wtf/root/lib/modules"
|
||||||
$(INSTALL) -m 0755 ./banger "$(DESTDIR)$(BINDIR)/banger"
|
$(INSTALL) -m 0755 ./banger "$(DESTDIR)$(BINDIR)/banger"
|
||||||
$(INSTALL) -m 0755 ./bangerd "$(DESTDIR)$(BINDIR)/bangerd"
|
$(INSTALL) -m 0755 ./bangerd "$(DESTDIR)$(BINDIR)/bangerd"
|
||||||
|
@for path in $(RUNTIME_EXECUTABLES); do \
|
||||||
|
$(INSTALL) -m 0755 "$$path" "$(DESTDIR)$(RUNTIMEDIR)/$$path"; \
|
||||||
|
done
|
||||||
|
@for path in $(RUNTIME_DATA_FILES) $(RUNTIME_BOOT_FILES); do \
|
||||||
|
$(INSTALL) -m 0644 "$$path" "$(DESTDIR)$(RUNTIMEDIR)/$$path"; \
|
||||||
|
done
|
||||||
|
$(INSTALL) -m 0600 id_ed25519 "$(DESTDIR)$(RUNTIMEDIR)/id_ed25519"
|
||||||
|
cp -a "$(RUNTIME_MODULES_DIR)" "$(DESTDIR)$(RUNTIMEDIR)/wtf/root/lib/modules/"
|
||||||
|
|
||||||
rootfs:
|
rootfs:
|
||||||
./make-rootfs.sh
|
./make-rootfs.sh
|
||||||
|
|
|
||||||
23
customize.sh
23
customize.sh
|
|
@ -29,19 +29,20 @@ parse_size() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
RUNTIME_DIR="${BANGER_RUNTIME_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
|
||||||
source "$DIR/dns.sh"
|
source "$RUNTIME_DIR/dns.sh"
|
||||||
source "$DIR/packages.sh"
|
source "$RUNTIME_DIR/packages.sh"
|
||||||
STATE="$DIR/state"
|
STATE="${BANGER_STATE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/banger/image-build}"
|
||||||
VM_ROOT="$STATE/vms"
|
VM_ROOT="$STATE/vms"
|
||||||
mkdir -p "$VM_ROOT"
|
mkdir -p "$VM_ROOT"
|
||||||
|
|
||||||
BASE_ROOTFS="$DIR/rootfs.ext4"
|
BASE_ROOTFS="$RUNTIME_DIR/rootfs.ext4"
|
||||||
FC_BIN="$DIR/firecracker"
|
FC_BIN="$RUNTIME_DIR/firecracker"
|
||||||
|
|
||||||
KERNEL="$DIR/wtf/root/boot/vmlinux-6.8.0-94-generic"
|
KERNEL="$RUNTIME_DIR/wtf/root/boot/vmlinux-6.8.0-94-generic"
|
||||||
INITRD="$DIR/wtf/root/boot/initrd.img-6.8.0-94-generic"
|
INITRD="$RUNTIME_DIR/wtf/root/boot/initrd.img-6.8.0-94-generic"
|
||||||
SSH_KEY="$DIR/id_ed25519"
|
SSH_KEY="$RUNTIME_DIR/id_ed25519"
|
||||||
|
NAT_SCRIPT="$RUNTIME_DIR/nat.sh"
|
||||||
|
|
||||||
BR_DEV="br-fc"
|
BR_DEV="br-fc"
|
||||||
BR_IP="172.16.0.1"
|
BR_IP="172.16.0.1"
|
||||||
|
|
@ -52,7 +53,7 @@ BASE_ROOTFS=""
|
||||||
OUT_ROOTFS=""
|
OUT_ROOTFS=""
|
||||||
SIZE_SPEC=""
|
SIZE_SPEC=""
|
||||||
INSTALL_DOCKER=0
|
INSTALL_DOCKER=0
|
||||||
MODULES_DIR="$DIR/wtf/root/lib/modules/6.8.0-94-generic"
|
MODULES_DIR="$RUNTIME_DIR/wtf/root/lib/modules/6.8.0-94-generic"
|
||||||
PACKAGES_FILE="$(banger_packages_file)"
|
PACKAGES_FILE="$(banger_packages_file)"
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
|
@ -304,7 +305,7 @@ jq -n \
|
||||||
> "$VM_DIR/vm.json"
|
> "$VM_DIR/vm.json"
|
||||||
|
|
||||||
log "enabling NAT for customization"
|
log "enabling NAT for customization"
|
||||||
sudo -E ./nat.sh up "$VM_TAG" >/dev/null
|
sudo -E "$NAT_SCRIPT" up "$VM_TAG" >/dev/null
|
||||||
|
|
||||||
log "waiting for SSH"
|
log "waiting for SSH"
|
||||||
SSH_READY=0
|
SSH_READY=0
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ func newVMCommand() *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func newVMCreateCommand() *cobra.Command {
|
func newVMCreateCommand() *cobra.Command {
|
||||||
var params api.VMCreateParams
|
var params api.VMCreateParams
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
|
@ -372,6 +373,9 @@ func newImageBuildCommand() *cobra.Command {
|
||||||
Short: "Build an image",
|
Short: "Build an image",
|
||||||
Args: noArgsUsage("usage: banger image build"),
|
Args: noArgsUsage("usage: banger image build"),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := absolutizeImageBuildPaths(¶ms); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := system.EnsureSudo(cmd.Context()); err != nil {
|
if err := system.EnsureSudo(cmd.Context()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -631,14 +635,28 @@ func sshCommandArgs(cfg model.DaemonConfig, guestIP string, extra []string) ([]s
|
||||||
return nil, errors.New("vm has no guest IP")
|
return nil, errors.New("vm has no guest IP")
|
||||||
}
|
}
|
||||||
args := []string{}
|
args := []string{}
|
||||||
if cfg.RepoRoot != "" {
|
if cfg.SSHKeyPath != "" {
|
||||||
args = append(args, "-i", filepath.Join(cfg.RepoRoot, "id_ed25519"))
|
args = append(args, "-i", cfg.SSHKeyPath)
|
||||||
}
|
}
|
||||||
args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "root@"+guestIP)
|
args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "root@"+guestIP)
|
||||||
args = append(args, extra...)
|
args = append(args, extra...)
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func absolutizeImageBuildPaths(params *api.ImageBuildParams) error {
|
||||||
|
var err error
|
||||||
|
for _, value := range []*string{¶ms.BaseRootfs, ¶ms.KernelPath, ¶ms.InitrdPath, ¶ms.ModulesDir} {
|
||||||
|
if *value == "" || filepath.IsAbs(*value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
*value, err = filepath.Abs(*value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func printJSON(out anyWriter, v any) error {
|
func printJSON(out anyWriter, v any) error {
|
||||||
data, err := json.MarshalIndent(v, "", " ")
|
data, err := json.MarshalIndent(v, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"banger/internal/api"
|
||||||
"banger/internal/model"
|
"banger/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,6 +39,7 @@ func TestVMCreateFlagsExist(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestVMSetParamsFromFlags(t *testing.T) {
|
func TestVMSetParamsFromFlags(t *testing.T) {
|
||||||
params, err := vmSetParamsFromFlags("devbox", 4, 2048, "16G", true, false)
|
params, err := vmSetParamsFromFlags("devbox", 4, 2048, "16G", true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -64,12 +66,12 @@ func TestVMSetParamsFromFlagsConflict(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSSHCommandArgs(t *testing.T) {
|
func TestSSHCommandArgs(t *testing.T) {
|
||||||
args, err := sshCommandArgs(model.DaemonConfig{RepoRoot: "/repo"}, "172.16.0.2", []string{"--", "uname", "-a"})
|
args, err := sshCommandArgs(model.DaemonConfig{SSHKeyPath: "/bundle/id_ed25519"}, "172.16.0.2", []string{"--", "uname", "-a"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("sshCommandArgs: %v", err)
|
t.Fatalf("sshCommandArgs: %v", err)
|
||||||
}
|
}
|
||||||
want := []string{
|
want := []string{
|
||||||
"-i", "/repo/id_ed25519",
|
"-i", "/bundle/id_ed25519",
|
||||||
"-o", "StrictHostKeyChecking=no",
|
"-o", "StrictHostKeyChecking=no",
|
||||||
"-o", "UserKnownHostsFile=/dev/null",
|
"-o", "UserKnownHostsFile=/dev/null",
|
||||||
"root@172.16.0.2",
|
"root@172.16.0.2",
|
||||||
|
|
@ -127,3 +129,37 @@ func TestDaemonOutdated(t *testing.T) {
|
||||||
t.Fatal("expected replaced daemon executable to be outdated")
|
t.Fatal("expected replaced daemon executable to be outdated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAbsolutizeImageBuildPaths(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
prev, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("getwd: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.Chdir(dir); err != nil {
|
||||||
|
t.Fatalf("chdir: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.Chdir(prev)
|
||||||
|
})
|
||||||
|
|
||||||
|
params := api.ImageBuildParams{
|
||||||
|
BaseRootfs: "images/base.ext4",
|
||||||
|
KernelPath: "/kernel",
|
||||||
|
InitrdPath: "boot/initrd.img",
|
||||||
|
ModulesDir: "modules",
|
||||||
|
}
|
||||||
|
if err := absolutizeImageBuildPaths(¶ms); err != nil {
|
||||||
|
t.Fatalf("absolutizeImageBuildPaths: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := api.ImageBuildParams{
|
||||||
|
BaseRootfs: filepath.Join(dir, "images/base.ext4"),
|
||||||
|
KernelPath: "/kernel",
|
||||||
|
InitrdPath: filepath.Join(dir, "boot/initrd.img"),
|
||||||
|
ModulesDir: filepath.Join(dir, "modules"),
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(params, want) {
|
||||||
|
t.Fatalf("params = %+v, want %+v", params, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type fileConfig struct {
|
type fileConfig struct {
|
||||||
|
RuntimeDir string `toml:"runtime_dir"`
|
||||||
RepoRoot string `toml:"repo_root"`
|
RepoRoot string `toml:"repo_root"`
|
||||||
|
FirecrackerBin string `toml:"firecracker_bin"`
|
||||||
|
SSHKeyPath string `toml:"ssh_key_path"`
|
||||||
|
NamegenPath string `toml:"namegen_path"`
|
||||||
|
CustomizeScript string `toml:"customize_script"`
|
||||||
DefaultImageName string `toml:"default_image_name"`
|
DefaultImageName string `toml:"default_image_name"`
|
||||||
|
DefaultRootfs string `toml:"default_rootfs"`
|
||||||
DefaultBaseRootfs string `toml:"default_base_rootfs"`
|
DefaultBaseRootfs string `toml:"default_base_rootfs"`
|
||||||
DefaultKernel string `toml:"default_kernel"`
|
DefaultKernel string `toml:"default_kernel"`
|
||||||
DefaultInitrd string `toml:"default_initrd"`
|
DefaultInitrd string `toml:"default_initrd"`
|
||||||
|
|
@ -30,7 +36,6 @@ type fileConfig struct {
|
||||||
|
|
||||||
func Load(layout paths.Layout) (model.DaemonConfig, error) {
|
func Load(layout paths.Layout) (model.DaemonConfig, error) {
|
||||||
cfg := model.DaemonConfig{
|
cfg := model.DaemonConfig{
|
||||||
RepoRoot: paths.DetectRepoRoot(),
|
|
||||||
AutoStopStaleAfter: 0,
|
AutoStopStaleAfter: 0,
|
||||||
StatsPollInterval: model.DefaultStatsPollInterval,
|
StatsPollInterval: model.DefaultStatsPollInterval,
|
||||||
MetricsPollInterval: model.DefaultMetricsPollInterval,
|
MetricsPollInterval: model.DefaultMetricsPollInterval,
|
||||||
|
|
@ -40,39 +45,45 @@ func Load(layout paths.Layout) (model.DaemonConfig, error) {
|
||||||
DefaultDNS: model.DefaultDNS,
|
DefaultDNS: model.DefaultDNS,
|
||||||
DefaultImageName: "default",
|
DefaultImageName: "default",
|
||||||
}
|
}
|
||||||
if cfg.RepoRoot != "" {
|
|
||||||
cfg.DefaultBaseRootfs = filepath.Join(cfg.RepoRoot, "rootfs.ext4")
|
|
||||||
cfg.DefaultKernel = filepath.Join(cfg.RepoRoot, "wtf/root/boot/vmlinux-6.8.0-94-generic")
|
|
||||||
cfg.DefaultInitrd = filepath.Join(cfg.RepoRoot, "wtf/root/boot/initrd.img-6.8.0-94-generic")
|
|
||||||
cfg.DefaultModulesDir = filepath.Join(cfg.RepoRoot, "wtf/root/lib/modules/6.8.0-94-generic")
|
|
||||||
cfg.DefaultPackagesFile = filepath.Join(cfg.RepoRoot, "packages.apt")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := filepath.Join(layout.ConfigDir, "config.toml")
|
path := filepath.Join(layout.ConfigDir, "config.toml")
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
if info.IsDir() {
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
var file fileConfig
|
var file fileConfig
|
||||||
if err := toml.Unmarshal(data, &file); err != nil {
|
if err != nil {
|
||||||
return cfg, err
|
if !os.IsNotExist(err) {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
} else if !info.IsDir() {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
|
if err := toml.Unmarshal(data, &file); err != nil {
|
||||||
|
return cfg, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if file.RepoRoot != "" {
|
|
||||||
cfg.RepoRoot = file.RepoRoot
|
cfg.RuntimeDir = paths.ResolveRuntimeDir(file.RuntimeDir, file.RepoRoot)
|
||||||
|
applyRuntimeDefaults(&cfg)
|
||||||
|
|
||||||
|
if file.FirecrackerBin != "" {
|
||||||
|
cfg.FirecrackerBin = file.FirecrackerBin
|
||||||
|
}
|
||||||
|
if file.SSHKeyPath != "" {
|
||||||
|
cfg.SSHKeyPath = file.SSHKeyPath
|
||||||
|
}
|
||||||
|
if file.NamegenPath != "" {
|
||||||
|
cfg.NamegenPath = file.NamegenPath
|
||||||
|
}
|
||||||
|
if file.CustomizeScript != "" {
|
||||||
|
cfg.CustomizeScript = file.CustomizeScript
|
||||||
}
|
}
|
||||||
if file.DefaultImageName != "" {
|
if file.DefaultImageName != "" {
|
||||||
cfg.DefaultImageName = file.DefaultImageName
|
cfg.DefaultImageName = file.DefaultImageName
|
||||||
}
|
}
|
||||||
|
if file.DefaultRootfs != "" {
|
||||||
|
cfg.DefaultRootfs = file.DefaultRootfs
|
||||||
|
}
|
||||||
if file.DefaultBaseRootfs != "" {
|
if file.DefaultBaseRootfs != "" {
|
||||||
cfg.DefaultBaseRootfs = file.DefaultBaseRootfs
|
cfg.DefaultBaseRootfs = file.DefaultBaseRootfs
|
||||||
}
|
}
|
||||||
|
|
@ -123,3 +134,48 @@ func Load(layout paths.Layout) (model.DaemonConfig, error) {
|
||||||
}
|
}
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyRuntimeDefaults(cfg *model.DaemonConfig) {
|
||||||
|
if cfg.RuntimeDir == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg.FirecrackerBin = defaultRuntimePath(cfg.FirecrackerBin, cfg.RuntimeDir, "firecracker")
|
||||||
|
cfg.SSHKeyPath = defaultRuntimePath(cfg.SSHKeyPath, cfg.RuntimeDir, "id_ed25519")
|
||||||
|
cfg.NamegenPath = defaultRuntimePath(cfg.NamegenPath, cfg.RuntimeDir, "namegen")
|
||||||
|
cfg.CustomizeScript = defaultRuntimePath(cfg.CustomizeScript, cfg.RuntimeDir, "customize.sh")
|
||||||
|
cfg.DefaultKernel = defaultRuntimePath(cfg.DefaultKernel, cfg.RuntimeDir, "wtf/root/boot/vmlinux-6.8.0-94-generic")
|
||||||
|
cfg.DefaultInitrd = defaultRuntimePath(cfg.DefaultInitrd, cfg.RuntimeDir, "wtf/root/boot/initrd.img-6.8.0-94-generic")
|
||||||
|
cfg.DefaultModulesDir = defaultRuntimePath(cfg.DefaultModulesDir, cfg.RuntimeDir, "wtf/root/lib/modules/6.8.0-94-generic")
|
||||||
|
cfg.DefaultPackagesFile = defaultRuntimePath(cfg.DefaultPackagesFile, cfg.RuntimeDir, "packages.apt")
|
||||||
|
if cfg.DefaultRootfs == "" {
|
||||||
|
cfg.DefaultRootfs = firstExistingRuntimePath(
|
||||||
|
filepath.Join(cfg.RuntimeDir, "rootfs-docker.ext4"),
|
||||||
|
filepath.Join(cfg.RuntimeDir, "rootfs.ext4"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if cfg.DefaultBaseRootfs == "" {
|
||||||
|
cfg.DefaultBaseRootfs = firstExistingRuntimePath(
|
||||||
|
filepath.Join(cfg.RuntimeDir, "rootfs.ext4"),
|
||||||
|
cfg.DefaultRootfs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultRuntimePath(current, runtimeDir, relative string) string {
|
||||||
|
if current != "" {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
return filepath.Join(runtimeDir, relative)
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstExistingRuntimePath(paths ...string) string {
|
||||||
|
for _, candidate := range paths {
|
||||||
|
if candidate == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(candidate); err == nil {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
|
||||||
72
internal/config/config_test.go
Normal file
72
internal/config/config_test.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"banger/internal/paths"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadDerivesArtifactPathsFromRuntimeDir(t *testing.T) {
|
||||||
|
runtimeDir := t.TempDir()
|
||||||
|
for _, rel := range []string{
|
||||||
|
"firecracker",
|
||||||
|
"id_ed25519",
|
||||||
|
"namegen",
|
||||||
|
"customize.sh",
|
||||||
|
"packages.apt",
|
||||||
|
"rootfs-docker.ext4",
|
||||||
|
"wtf/root/boot/vmlinux-6.8.0-94-generic",
|
||||||
|
"wtf/root/boot/initrd.img-6.8.0-94-generic",
|
||||||
|
"wtf/root/lib/modules/6.8.0-94-generic/modules.dep",
|
||||||
|
} {
|
||||||
|
path := filepath.Join(runtimeDir, rel)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||||
|
t.Fatalf("mkdir %s: %v", filepath.Dir(path), err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(path, []byte("test"), 0o644); err != nil {
|
||||||
|
t.Fatalf("write %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Setenv("BANGER_RUNTIME_DIR", runtimeDir)
|
||||||
|
cfg, err := Load(paths.Layout{ConfigDir: t.TempDir()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.RuntimeDir != runtimeDir {
|
||||||
|
t.Fatalf("RuntimeDir = %q, want %q", cfg.RuntimeDir, runtimeDir)
|
||||||
|
}
|
||||||
|
if cfg.FirecrackerBin != filepath.Join(runtimeDir, "firecracker") {
|
||||||
|
t.Fatalf("FirecrackerBin = %q", cfg.FirecrackerBin)
|
||||||
|
}
|
||||||
|
if cfg.SSHKeyPath != filepath.Join(runtimeDir, "id_ed25519") {
|
||||||
|
t.Fatalf("SSHKeyPath = %q", cfg.SSHKeyPath)
|
||||||
|
}
|
||||||
|
if cfg.NamegenPath != filepath.Join(runtimeDir, "namegen") {
|
||||||
|
t.Fatalf("NamegenPath = %q", cfg.NamegenPath)
|
||||||
|
}
|
||||||
|
if cfg.CustomizeScript != filepath.Join(runtimeDir, "customize.sh") {
|
||||||
|
t.Fatalf("CustomizeScript = %q", cfg.CustomizeScript)
|
||||||
|
}
|
||||||
|
if cfg.DefaultRootfs != filepath.Join(runtimeDir, "rootfs-docker.ext4") {
|
||||||
|
t.Fatalf("DefaultRootfs = %q", cfg.DefaultRootfs)
|
||||||
|
}
|
||||||
|
if cfg.DefaultBaseRootfs != filepath.Join(runtimeDir, "rootfs-docker.ext4") {
|
||||||
|
t.Fatalf("DefaultBaseRootfs = %q", cfg.DefaultBaseRootfs)
|
||||||
|
}
|
||||||
|
if cfg.DefaultKernel != filepath.Join(runtimeDir, "wtf/root/boot/vmlinux-6.8.0-94-generic") {
|
||||||
|
t.Fatalf("DefaultKernel = %q", cfg.DefaultKernel)
|
||||||
|
}
|
||||||
|
if cfg.DefaultInitrd != filepath.Join(runtimeDir, "wtf/root/boot/initrd.img-6.8.0-94-generic") {
|
||||||
|
t.Fatalf("DefaultInitrd = %q", cfg.DefaultInitrd)
|
||||||
|
}
|
||||||
|
if cfg.DefaultModulesDir != filepath.Join(runtimeDir, "wtf/root/lib/modules/6.8.0-94-generic") {
|
||||||
|
t.Fatalf("DefaultModulesDir = %q", cfg.DefaultModulesDir)
|
||||||
|
}
|
||||||
|
if cfg.DefaultPackagesFile != filepath.Join(runtimeDir, "packages.apt") {
|
||||||
|
t.Fatalf("DefaultPackagesFile = %q", cfg.DefaultPackagesFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -267,13 +267,13 @@ func (d *Daemon) backgroundLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) ensureDefaultImage(ctx context.Context) error {
|
func (d *Daemon) ensureDefaultImage(ctx context.Context) error {
|
||||||
if d.config.DefaultImageName == "" || d.config.RepoRoot == "" {
|
if d.config.DefaultImageName == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if _, err := d.store.GetImageByName(ctx, d.config.DefaultImageName); err == nil {
|
if _, err := d.store.GetImageByName(ctx, d.config.DefaultImageName); err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
rootfs := filepath.Join(d.config.RepoRoot, "rootfs-docker.ext4")
|
rootfs := d.config.DefaultRootfs
|
||||||
kernel := d.config.DefaultKernel
|
kernel := d.config.DefaultKernel
|
||||||
initrd := d.config.DefaultInitrd
|
initrd := d.config.DefaultInitrd
|
||||||
if !exists(rootfs) || !exists(kernel) {
|
if !exists(rootfs) || !exists(kernel) {
|
||||||
|
|
|
||||||
54
internal/daemon/daemon_test.go
Normal file
54
internal/daemon/daemon_test.go
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"banger/internal/model"
|
||||||
|
"banger/internal/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnsureDefaultImageUsesConfiguredDefaultRootfs(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
rootfs := filepath.Join(dir, "rootfs-docker.ext4")
|
||||||
|
kernel := filepath.Join(dir, "vmlinux")
|
||||||
|
for _, path := range []string{rootfs, kernel} {
|
||||||
|
if err := os.WriteFile(path, []byte("test"), 0o644); err != nil {
|
||||||
|
t.Fatalf("write %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := store.Open(filepath.Join(dir, "state.db"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("open store: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = db.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
d := &Daemon{
|
||||||
|
config: model.DaemonConfig{
|
||||||
|
DefaultImageName: "default",
|
||||||
|
DefaultRootfs: rootfs,
|
||||||
|
DefaultKernel: kernel,
|
||||||
|
},
|
||||||
|
store: db,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.ensureDefaultImage(context.Background()); err != nil {
|
||||||
|
t.Fatalf("ensureDefaultImage: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
image, err := db.GetImageByName(context.Background(), "default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetImageByName: %v", err)
|
||||||
|
}
|
||||||
|
if image.RootfsPath != rootfs {
|
||||||
|
t.Fatalf("RootfsPath = %q, want %q", image.RootfsPath, rootfs)
|
||||||
|
}
|
||||||
|
if image.KernelPath != kernel {
|
||||||
|
t.Fatalf("KernelPath = %q, want %q", image.KernelPath, kernel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,9 +22,6 @@ func (d *Daemon) BuildImage(ctx context.Context, params api.ImageBuildParams) (m
|
||||||
if _, err := d.FindImage(ctx, name); err == nil {
|
if _, err := d.FindImage(ctx, name); err == nil {
|
||||||
return model.Image{}, fmt.Errorf("image name already exists: %s", name)
|
return model.Image{}, fmt.Errorf("image name already exists: %s", name)
|
||||||
}
|
}
|
||||||
if d.config.RepoRoot == "" {
|
|
||||||
return model.Image{}, fmt.Errorf("repo root not found; set repo_root in config.toml")
|
|
||||||
}
|
|
||||||
baseRootfs := params.BaseRootfs
|
baseRootfs := params.BaseRootfs
|
||||||
if baseRootfs == "" {
|
if baseRootfs == "" {
|
||||||
baseRootfs = d.config.DefaultBaseRootfs
|
baseRootfs = d.config.DefaultBaseRootfs
|
||||||
|
|
@ -42,7 +39,10 @@ func (d *Daemon) BuildImage(ctx context.Context, params api.ImageBuildParams) (m
|
||||||
return model.Image{}, err
|
return model.Image{}, err
|
||||||
}
|
}
|
||||||
rootfsPath := filepath.Join(artifactDir, "rootfs.ext4")
|
rootfsPath := filepath.Join(artifactDir, "rootfs.ext4")
|
||||||
script := filepath.Join(d.config.RepoRoot, "customize.sh")
|
script := d.config.CustomizeScript
|
||||||
|
if script == "" {
|
||||||
|
return model.Image{}, fmt.Errorf("customize script not configured; set runtime_dir or customize_script in config.toml")
|
||||||
|
}
|
||||||
if _, err := os.Stat(script); err != nil {
|
if _, err := os.Stat(script); err != nil {
|
||||||
return model.Image{}, fmt.Errorf("customize.sh not found at %s", script)
|
return model.Image{}, fmt.Errorf("customize.sh not found at %s", script)
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +78,12 @@ func (d *Daemon) BuildImage(ctx context.Context, params api.ImageBuildParams) (m
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Dir = d.config.RepoRoot
|
cmd.Dir = d.layout.StateDir
|
||||||
|
cmd.Env = append(
|
||||||
|
os.Environ(),
|
||||||
|
"BANGER_RUNTIME_DIR="+d.config.RuntimeDir,
|
||||||
|
"BANGER_STATE_DIR="+filepath.Join(d.layout.StateDir, "image-build"),
|
||||||
|
)
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
_ = os.RemoveAll(artifactDir)
|
_ = os.RemoveAll(artifactDir)
|
||||||
return model.Image{}, err
|
return model.Image{}, err
|
||||||
|
|
|
||||||
|
|
@ -546,10 +546,10 @@ func (d *Daemon) createTap(ctx context.Context, tap string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) firecrackerBinary() (string, error) {
|
func (d *Daemon) firecrackerBinary() (string, error) {
|
||||||
if d.config.RepoRoot == "" {
|
if d.config.FirecrackerBin == "" {
|
||||||
return "", errors.New("repo root not detected")
|
return "", errors.New("firecracker binary not configured; set runtime_dir or firecracker_bin in config.toml")
|
||||||
}
|
}
|
||||||
path := filepath.Join(d.config.RepoRoot, "firecracker")
|
path := d.config.FirecrackerBin
|
||||||
if !exists(path) {
|
if !exists(path) {
|
||||||
return "", fmt.Errorf("firecracker binary not found at %s", path)
|
return "", fmt.Errorf("firecracker binary not found at %s", path)
|
||||||
}
|
}
|
||||||
|
|
@ -689,15 +689,12 @@ func (d *Daemon) requireStartPrereqs(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Daemon) generateName(ctx context.Context) (string, error) {
|
func (d *Daemon) generateName(ctx context.Context) (string, error) {
|
||||||
if d.config.RepoRoot != "" {
|
if exists(d.config.NamegenPath) {
|
||||||
namegen := filepath.Join(d.config.RepoRoot, "namegen")
|
out, err := d.runner.Run(ctx, d.config.NamegenPath)
|
||||||
if exists(namegen) {
|
if err == nil {
|
||||||
out, err := d.runner.Run(ctx, namegen)
|
name := strings.TrimSpace(string(out))
|
||||||
if err == nil {
|
if name != "" {
|
||||||
name := strings.TrimSpace(string(out))
|
return name, nil
|
||||||
if name != "" {
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DaemonConfig struct {
|
type DaemonConfig struct {
|
||||||
RepoRoot string
|
RuntimeDir string
|
||||||
|
FirecrackerBin string
|
||||||
|
SSHKeyPath string
|
||||||
|
NamegenPath string
|
||||||
|
CustomizeScript string
|
||||||
AutoStopStaleAfter time.Duration
|
AutoStopStaleAfter time.Duration
|
||||||
StatsPollInterval time.Duration
|
StatsPollInterval time.Duration
|
||||||
MetricsPollInterval time.Duration
|
MetricsPollInterval time.Duration
|
||||||
|
|
@ -44,6 +48,7 @@ type DaemonConfig struct {
|
||||||
CIDR string
|
CIDR string
|
||||||
DefaultDNS string
|
DefaultDNS string
|
||||||
DefaultImageName string
|
DefaultImageName string
|
||||||
|
DefaultRootfs string
|
||||||
DefaultBaseRootfs string
|
DefaultBaseRootfs string
|
||||||
DefaultKernel string
|
DefaultKernel string
|
||||||
DefaultInitrd string
|
DefaultInitrd string
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -66,57 +65,64 @@ func Ensure(layout Layout) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetectRepoRoot() string {
|
var executablePath = os.Executable
|
||||||
if env := os.Getenv("BANGER_REPO_ROOT"); env != "" {
|
|
||||||
return env
|
func ResolveRuntimeDir(configuredRuntimeDir, deprecatedRepoRoot string) string {
|
||||||
}
|
for _, candidate := range []string{
|
||||||
candidates := []string{}
|
os.Getenv("BANGER_RUNTIME_DIR"),
|
||||||
if wd, err := os.Getwd(); err == nil {
|
os.Getenv("BANGER_REPO_ROOT"),
|
||||||
candidates = append(candidates, wd)
|
configuredRuntimeDir,
|
||||||
}
|
deprecatedRepoRoot,
|
||||||
if exe, err := os.Executable(); err == nil {
|
} {
|
||||||
candidates = append(candidates, filepath.Dir(exe))
|
if candidate = strings.TrimSpace(candidate); candidate != "" {
|
||||||
}
|
return filepath.Clean(candidate)
|
||||||
if look, err := exec.LookPath("firecracker"); err == nil {
|
|
||||||
candidates = append(candidates, filepath.Dir(look))
|
|
||||||
}
|
|
||||||
for _, candidate := range candidates {
|
|
||||||
if root := walkForRepoRoot(candidate); root != "" {
|
|
||||||
return root
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exe, err := executablePath()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
exeDir := filepath.Dir(exe)
|
||||||
|
if filepath.Base(exeDir) == "bin" {
|
||||||
|
installRuntimeDir := filepath.Clean(filepath.Join(exeDir, "..", "lib", "banger"))
|
||||||
|
if HasRuntimeBundle(installRuntimeDir) {
|
||||||
|
return installRuntimeDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if HasRuntimeBundle(exeDir) {
|
||||||
|
return exeDir
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func walkForRepoRoot(start string) string {
|
func HasRuntimeBundle(dir string) bool {
|
||||||
current := start
|
if strings.TrimSpace(dir) == "" {
|
||||||
for {
|
return false
|
||||||
if hasRepoArtifacts(current) {
|
}
|
||||||
return current
|
required := []string{
|
||||||
}
|
"firecracker",
|
||||||
parent := filepath.Dir(current)
|
"customize.sh",
|
||||||
if parent == current {
|
"packages.apt",
|
||||||
return ""
|
"wtf/root/boot/vmlinux-6.8.0-94-generic",
|
||||||
}
|
|
||||||
current = parent
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func hasRepoArtifacts(dir string) bool {
|
|
||||||
required := []string{"firecracker", "README.md"}
|
|
||||||
for _, name := range required {
|
for _, name := range required {
|
||||||
if _, err := os.Stat(filepath.Join(dir, name)); err != nil {
|
if _, err := os.Stat(filepath.Join(dir, name)); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
for _, name := range []string{"rootfs-docker.ext4", "rootfs.ext4"} {
|
||||||
|
if _, err := os.Stat(filepath.Join(dir, name)); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func BangerdPath() (string, error) {
|
func BangerdPath() (string, error) {
|
||||||
if env := os.Getenv("BANGER_DAEMON_BIN"); env != "" {
|
if env := os.Getenv("BANGER_DAEMON_BIN"); env != "" {
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
exe, err := os.Executable()
|
exe, err := executablePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -129,16 +135,6 @@ func BangerdPath() (string, error) {
|
||||||
return candidate, nil
|
return candidate, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if root := DetectRepoRoot(); root != "" {
|
|
||||||
for _, candidate := range []string{
|
|
||||||
filepath.Join(root, "bangerd"),
|
|
||||||
filepath.Join(root, "bangerd.exe"),
|
|
||||||
} {
|
|
||||||
if _, err := os.Stat(candidate); err == nil {
|
|
||||||
return candidate, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", errors.New("bangerd binary not found next to banger; build ./cmd/bangerd")
|
return "", errors.New("bangerd binary not found next to banger; build ./cmd/bangerd")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
69
internal/paths/paths_test.go
Normal file
69
internal/paths/paths_test.go
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package paths
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResolveRuntimeDirPrefersEnv(t *testing.T) {
|
||||||
|
t.Setenv("BANGER_RUNTIME_DIR", "/env/runtime")
|
||||||
|
|
||||||
|
if got := ResolveRuntimeDir("/config/runtime", "/deprecated/repo"); got != "/env/runtime" {
|
||||||
|
t.Fatalf("ResolveRuntimeDir() = %q, want /env/runtime", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveRuntimeDirUsesInstalledLayout(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
runtimeDir := filepath.Join(root, "lib", "banger")
|
||||||
|
createRuntimeBundle(t, runtimeDir)
|
||||||
|
|
||||||
|
origExecutablePath := executablePath
|
||||||
|
executablePath = func() (string, error) {
|
||||||
|
return filepath.Join(root, "bin", "banger"), nil
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
executablePath = origExecutablePath
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := ResolveRuntimeDir("", ""); got != runtimeDir {
|
||||||
|
t.Fatalf("ResolveRuntimeDir() = %q, want %q", got, runtimeDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveRuntimeDirUsesExecutableDirectoryBundle(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
createRuntimeBundle(t, root)
|
||||||
|
|
||||||
|
origExecutablePath := executablePath
|
||||||
|
executablePath = func() (string, error) {
|
||||||
|
return filepath.Join(root, "banger"), nil
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
executablePath = origExecutablePath
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := ResolveRuntimeDir("", ""); got != root {
|
||||||
|
t.Fatalf("ResolveRuntimeDir() = %q, want %q", got, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRuntimeBundle(t *testing.T, runtimeDir string) {
|
||||||
|
t.Helper()
|
||||||
|
for _, rel := range []string{
|
||||||
|
"firecracker",
|
||||||
|
"customize.sh",
|
||||||
|
"packages.apt",
|
||||||
|
"rootfs-docker.ext4",
|
||||||
|
"wtf/root/boot/vmlinux-6.8.0-94-generic",
|
||||||
|
} {
|
||||||
|
path := filepath.Join(runtimeDir, rel)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||||
|
t.Fatalf("mkdir %s: %v", filepath.Dir(path), err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(path, []byte("test"), 0o644); err != nil {
|
||||||
|
t.Fatalf("write %s: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue