Stamp shared build metadata into banger binaries
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.
This commit is contained in:
parent
ea2db1e868
commit
f798e1db33
9 changed files with 219 additions and 13 deletions
10
Makefile
10
Makefile
|
|
@ -20,6 +20,10 @@ VOID_VM_NAME ?= void-dev
|
||||||
ALPINE_RELEASE ?= 3.23.3
|
ALPINE_RELEASE ?= 3.23.3
|
||||||
ALPINE_IMAGE_NAME ?= alpine
|
ALPINE_IMAGE_NAME ?= alpine
|
||||||
ALPINE_VM_NAME ?= alpine-dev
|
ALPINE_VM_NAME ?= alpine-dev
|
||||||
|
VERSION ?= $(shell git describe --tags --exact-match 2>/dev/null || echo dev)
|
||||||
|
COMMIT ?= $(shell git rev-parse --verify HEAD 2>/dev/null || echo unknown)
|
||||||
|
BUILT_AT ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||||
|
GO_LDFLAGS := -X banger/internal/buildinfo.Version=$(VERSION) -X banger/internal/buildinfo.Commit=$(COMMIT) -X banger/internal/buildinfo.BuiltAt=$(BUILT_AT)
|
||||||
|
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
|
@ -51,15 +55,15 @@ build: $(BINARIES)
|
||||||
|
|
||||||
$(BANGER_BIN): $(GO_SOURCES) go.mod go.sum
|
$(BANGER_BIN): $(GO_SOURCES) go.mod go.sum
|
||||||
mkdir -p "$(BUILD_BIN_DIR)"
|
mkdir -p "$(BUILD_BIN_DIR)"
|
||||||
$(GO) build -o "$(BANGER_BIN)" ./cmd/banger
|
$(GO) build -ldflags '$(GO_LDFLAGS)' -o "$(BANGER_BIN)" ./cmd/banger
|
||||||
|
|
||||||
$(BANGERD_BIN): $(GO_SOURCES) go.mod go.sum
|
$(BANGERD_BIN): $(GO_SOURCES) go.mod go.sum
|
||||||
mkdir -p "$(BUILD_BIN_DIR)"
|
mkdir -p "$(BUILD_BIN_DIR)"
|
||||||
$(GO) build -o "$(BANGERD_BIN)" ./cmd/bangerd
|
$(GO) build -ldflags '$(GO_LDFLAGS)' -o "$(BANGERD_BIN)" ./cmd/bangerd
|
||||||
|
|
||||||
$(VSOCK_AGENT_BIN): $(GO_SOURCES) go.mod go.sum
|
$(VSOCK_AGENT_BIN): $(GO_SOURCES) go.mod go.sum
|
||||||
mkdir -p "$(BUILD_BIN_DIR)"
|
mkdir -p "$(BUILD_BIN_DIR)"
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -o "$(VSOCK_AGENT_BIN)" ./cmd/banger-vsock-agent
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -ldflags '$(GO_LDFLAGS)' -o "$(VSOCK_AGENT_BIN)" ./cmd/banger-vsock-agent
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(GO) test ./...
|
$(GO) test ./...
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,15 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"banger/internal/buildinfo"
|
||||||
sdkvsock "github.com/firecracker-microvm/firecracker-go-sdk/vsock"
|
sdkvsock "github.com/firecracker-microvm/firecracker-go-sdk/vsock"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"banger/internal/vsockagent"
|
"banger/internal/vsockagent"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _, _, _ = buildinfo.Version, buildinfo.Commit, buildinfo.BuiltAt
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ type PingResult struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
PID int `json:"pid"`
|
PID int `json:"pid"`
|
||||||
WebURL string `json:"web_url,omitempty"`
|
WebURL string `json:"web_url,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Commit string `json:"commit,omitempty"`
|
||||||
|
BuiltAt string `json:"built_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShutdownResult struct {
|
type ShutdownResult struct {
|
||||||
|
|
|
||||||
34
internal/buildinfo/buildinfo.go
Normal file
34
internal/buildinfo/buildinfo.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package buildinfo
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
var (
|
||||||
|
Version = "dev"
|
||||||
|
Commit = "unknown"
|
||||||
|
BuiltAt = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
Version string
|
||||||
|
Commit string
|
||||||
|
BuiltAt string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Current() Info {
|
||||||
|
return Normalize(Version, Commit, BuiltAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Normalize(version, commit, builtAt string) Info {
|
||||||
|
return Info{
|
||||||
|
Version: normalizedValue(version, "dev"),
|
||||||
|
Commit: normalizedValue(commit, "unknown"),
|
||||||
|
BuiltAt: normalizedValue(builtAt, "unknown"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizedValue(value, fallback string) string {
|
||||||
|
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
33
internal/buildinfo/buildinfo_test.go
Normal file
33
internal/buildinfo/buildinfo_test.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package buildinfo
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNormalizeUsesFallbacks(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
info := Normalize("", " ", "\t")
|
||||||
|
if info.Version != "dev" {
|
||||||
|
t.Fatalf("Version = %q, want dev", info.Version)
|
||||||
|
}
|
||||||
|
if info.Commit != "unknown" {
|
||||||
|
t.Fatalf("Commit = %q, want unknown", info.Commit)
|
||||||
|
}
|
||||||
|
if info.BuiltAt != "unknown" {
|
||||||
|
t.Fatalf("BuiltAt = %q, want unknown", info.BuiltAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeTrimsValues(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
info := Normalize(" v1.2.3 ", " abc123 ", " 2026-03-22T12:00:00Z ")
|
||||||
|
if info.Version != "v1.2.3" {
|
||||||
|
t.Fatalf("Version = %q, want v1.2.3", info.Version)
|
||||||
|
}
|
||||||
|
if info.Commit != "abc123" {
|
||||||
|
t.Fatalf("Commit = %q, want abc123", info.Commit)
|
||||||
|
}
|
||||||
|
if info.BuiltAt != "2026-03-22T12:00:00Z" {
|
||||||
|
t.Fatalf("BuiltAt = %q, want 2026-03-22T12:00:00Z", info.BuiltAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"banger/internal/api"
|
"banger/internal/api"
|
||||||
|
"banger/internal/buildinfo"
|
||||||
"banger/internal/config"
|
"banger/internal/config"
|
||||||
"banger/internal/daemon"
|
"banger/internal/daemon"
|
||||||
"banger/internal/guest"
|
"banger/internal/guest"
|
||||||
|
|
@ -70,6 +71,9 @@ var (
|
||||||
vmHealthFunc = func(ctx context.Context, socketPath, idOrName string) (api.VMHealthResult, error) {
|
vmHealthFunc = func(ctx context.Context, socketPath, idOrName string) (api.VMHealthResult, error) {
|
||||||
return rpc.Call[api.VMHealthResult](ctx, socketPath, "vm.health", api.VMRefParams{IDOrName: idOrName})
|
return rpc.Call[api.VMHealthResult](ctx, socketPath, "vm.health", api.VMRefParams{IDOrName: idOrName})
|
||||||
}
|
}
|
||||||
|
daemonPingFunc = func(ctx context.Context, socketPath string) (api.PingResult, error) {
|
||||||
|
return rpc.Call[api.PingResult](ctx, socketPath, "ping", api.Empty{})
|
||||||
|
}
|
||||||
vmCreateBeginFunc = func(ctx context.Context, socketPath string, params api.VMCreateParams) (api.VMCreateBeginResult, error) {
|
vmCreateBeginFunc = func(ctx context.Context, socketPath string, params api.VMCreateParams) (api.VMCreateBeginResult, error) {
|
||||||
return rpc.Call[api.VMCreateBeginResult](ctx, socketPath, "vm.create.begin", params)
|
return rpc.Call[api.VMCreateBeginResult](ctx, socketPath, "vm.create.begin", params)
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +125,7 @@ func NewBangerCommand() *cobra.Command {
|
||||||
RunE: helpNoArgs,
|
RunE: helpNoArgs,
|
||||||
}
|
}
|
||||||
root.CompletionOptions.DisableDefaultCmd = true
|
root.CompletionOptions.DisableDefaultCmd = true
|
||||||
root.AddCommand(newDaemonCommand(), newDoctorCommand(), newVMCommand(), newImageCommand(), newInternalCommand())
|
root.AddCommand(newDaemonCommand(), newDoctorCommand(), newImageCommand(), newInternalCommand(), newVersionCommand(), newVMCommand())
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,6 +150,18 @@ func newDoctorCommand() *cobra.Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newVersionCommand() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Show banger build information",
|
||||||
|
Args: noArgsUsage("usage: banger version"),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := fmt.Fprint(cmd.OutOrStdout(), formatBuildInfoBlock(buildinfo.Current()))
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newInternalCommand() *cobra.Command {
|
func newInternalCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "internal",
|
Use: "internal",
|
||||||
|
|
@ -342,7 +358,7 @@ func newDaemonCommand() *cobra.Command {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ping, pingErr := rpc.Call[api.PingResult](cmd.Context(), layout.SocketPath, "ping", api.Empty{})
|
ping, pingErr := daemonPingFunc(cmd.Context(), layout.SocketPath)
|
||||||
if pingErr != nil {
|
if pingErr != nil {
|
||||||
if strings.TrimSpace(cfg.WebListenAddr) != "" {
|
if strings.TrimSpace(cfg.WebListenAddr) != "" {
|
||||||
_, err = fmt.Fprintf(cmd.OutOrStdout(), "stopped\nsocket: %s\nlog: %s\ndns: %s\nweb: http://%s\n", layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr, cfg.WebListenAddr)
|
_, err = fmt.Fprintf(cmd.OutOrStdout(), "stopped\nsocket: %s\nlog: %s\ndns: %s\nweb: http://%s\n", layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr, cfg.WebListenAddr)
|
||||||
|
|
@ -351,11 +367,12 @@ func newDaemonCommand() *cobra.Command {
|
||||||
_, err = fmt.Fprintf(cmd.OutOrStdout(), "stopped\nsocket: %s\nlog: %s\ndns: %s\n", layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr)
|
_, err = fmt.Fprintf(cmd.OutOrStdout(), "stopped\nsocket: %s\nlog: %s\ndns: %s\n", layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
info := buildinfo.Normalize(ping.Version, ping.Commit, ping.BuiltAt)
|
||||||
if strings.TrimSpace(ping.WebURL) != "" {
|
if strings.TrimSpace(ping.WebURL) != "" {
|
||||||
_, err = fmt.Fprintf(cmd.OutOrStdout(), "running\npid: %d\nsocket: %s\nlog: %s\ndns: %s\nweb: %s\n", ping.PID, layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr, ping.WebURL)
|
_, err = fmt.Fprintf(cmd.OutOrStdout(), "running\npid: %d\n%ssocket: %s\nlog: %s\ndns: %s\nweb: %s\n", ping.PID, formatBuildInfoBlock(info), layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr, ping.WebURL)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = fmt.Fprintf(cmd.OutOrStdout(), "running\npid: %d\nsocket: %s\nlog: %s\ndns: %s\n", ping.PID, layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr)
|
_, err = fmt.Fprintf(cmd.OutOrStdout(), "running\npid: %d\n%ssocket: %s\nlog: %s\ndns: %s\n", ping.PID, formatBuildInfoBlock(info), layout.SocketPath, layout.DaemonLog, vmdns.DefaultListenAddr)
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1141,7 +1158,7 @@ func ensureDaemon(ctx context.Context) (paths.Layout, model.DaemonConfig, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return paths.Layout{}, model.DaemonConfig{}, err
|
return paths.Layout{}, model.DaemonConfig{}, err
|
||||||
}
|
}
|
||||||
if ping, err := rpc.Call[api.PingResult](ctx, layout.SocketPath, "ping", api.Empty{}); err == nil {
|
if ping, err := daemonPingFunc(ctx, layout.SocketPath); err == nil {
|
||||||
if daemonOutdated(ping.PID) {
|
if daemonOutdated(ping.PID) {
|
||||||
if err := restartDaemon(ctx, layout, ping.PID); err != nil {
|
if err := restartDaemon(ctx, layout, ping.PID); err != nil {
|
||||||
return paths.Layout{}, model.DaemonConfig{}, err
|
return paths.Layout{}, model.DaemonConfig{}, err
|
||||||
|
|
@ -2067,3 +2084,7 @@ func relativeTime(t time.Time) string {
|
||||||
return fmt.Sprintf("%d weeks ago", int(delta.Hours()/(24*7)))
|
return fmt.Sprintf("%d weeks ago", int(delta.Hours()/(24*7)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatBuildInfoBlock(info buildinfo.Info) string {
|
||||||
|
return fmt.Sprintf("version: %s\ncommit: %s\nbuilt_at: %s\n", info.Version, info.Commit, info.BuiltAt)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"banger/internal/api"
|
"banger/internal/api"
|
||||||
|
"banger/internal/buildinfo"
|
||||||
"banger/internal/model"
|
"banger/internal/model"
|
||||||
"banger/internal/system"
|
"banger/internal/system"
|
||||||
)
|
)
|
||||||
|
|
@ -25,12 +26,36 @@ func TestNewBangerCommandHasExpectedSubcommands(t *testing.T) {
|
||||||
for _, sub := range cmd.Commands() {
|
for _, sub := range cmd.Commands() {
|
||||||
names = append(names, sub.Name())
|
names = append(names, sub.Name())
|
||||||
}
|
}
|
||||||
want := []string{"daemon", "doctor", "image", "internal", "vm"}
|
want := []string{"daemon", "doctor", "image", "internal", "version", "vm"}
|
||||||
if !reflect.DeepEqual(names, want) {
|
if !reflect.DeepEqual(names, want) {
|
||||||
t.Fatalf("subcommands = %v, want %v", names, want)
|
t.Fatalf("subcommands = %v, want %v", names, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVersionCommandPrintsBuildInfo(t *testing.T) {
|
||||||
|
cmd := NewBangerCommand()
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.SetOut(&stdout)
|
||||||
|
cmd.SetErr(&stdout)
|
||||||
|
cmd.SetArgs([]string{"version"})
|
||||||
|
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
|
t.Fatalf("Execute: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info := buildinfo.Current()
|
||||||
|
output := stdout.String()
|
||||||
|
for _, want := range []string{
|
||||||
|
"version: " + info.Version,
|
||||||
|
"commit: " + info.Commit,
|
||||||
|
"built_at: " + info.BuiltAt,
|
||||||
|
} {
|
||||||
|
if !strings.Contains(output, want) {
|
||||||
|
t.Fatalf("output = %q, want %q", output, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLegacyRemovedCommandIsRejected(t *testing.T) {
|
func TestLegacyRemovedCommandIsRejected(t *testing.T) {
|
||||||
cmd := NewBangerCommand()
|
cmd := NewBangerCommand()
|
||||||
cmd.SetArgs([]string{"tui"})
|
cmd.SetArgs([]string{"tui"})
|
||||||
|
|
@ -1222,6 +1247,55 @@ func TestDaemonStatusIncludesLogPathWhenStopped(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDaemonStatusIncludesDaemonBuildInfoWhenRunning(t *testing.T) {
|
||||||
|
origDaemonPing := daemonPingFunc
|
||||||
|
t.Cleanup(func() {
|
||||||
|
daemonPingFunc = origDaemonPing
|
||||||
|
})
|
||||||
|
|
||||||
|
configHome := filepath.Join(t.TempDir(), "config")
|
||||||
|
stateHome := filepath.Join(t.TempDir(), "state")
|
||||||
|
runtimeHome := filepath.Join(t.TempDir(), "runtime")
|
||||||
|
t.Setenv("XDG_CONFIG_HOME", configHome)
|
||||||
|
t.Setenv("XDG_STATE_HOME", stateHome)
|
||||||
|
t.Setenv("XDG_RUNTIME_DIR", runtimeHome)
|
||||||
|
|
||||||
|
daemonPingFunc = func(context.Context, string) (api.PingResult, error) {
|
||||||
|
return api.PingResult{
|
||||||
|
Status: "ok",
|
||||||
|
PID: 42,
|
||||||
|
WebURL: "http://127.0.0.1:7777",
|
||||||
|
Version: "v1.2.3",
|
||||||
|
Commit: "abc123",
|
||||||
|
BuiltAt: "2026-03-22T12:00:00Z",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := NewBangerCommand()
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.SetOut(&stdout)
|
||||||
|
cmd.SetErr(&stdout)
|
||||||
|
cmd.SetArgs([]string{"daemon", "status"})
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
|
t.Fatalf("Execute: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := stdout.String()
|
||||||
|
for _, want := range []string{
|
||||||
|
"running\n",
|
||||||
|
"pid: 42",
|
||||||
|
"version: v1.2.3",
|
||||||
|
"commit: abc123",
|
||||||
|
"built_at: 2026-03-22T12:00:00Z",
|
||||||
|
"log: " + filepath.Join(stateHome, "banger", "bangerd.log"),
|
||||||
|
"web: http://127.0.0.1:7777",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(output, want) {
|
||||||
|
t.Fatalf("output = %q, want %q", output, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildDaemonCommandIsDetachedFromCallerContext(t *testing.T) {
|
func TestBuildDaemonCommandIsDetachedFromCallerContext(t *testing.T) {
|
||||||
cmd := buildDaemonCommand("/tmp/bangerd")
|
cmd := buildDaemonCommand("/tmp/bangerd")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"banger/internal/api"
|
"banger/internal/api"
|
||||||
|
"banger/internal/buildinfo"
|
||||||
"banger/internal/config"
|
"banger/internal/config"
|
||||||
"banger/internal/model"
|
"banger/internal/model"
|
||||||
"banger/internal/paths"
|
"banger/internal/paths"
|
||||||
|
|
@ -250,7 +251,15 @@ func (d *Daemon) dispatch(ctx context.Context, req rpc.Request) rpc.Response {
|
||||||
}
|
}
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case "ping":
|
case "ping":
|
||||||
result, _ := rpc.NewResult(api.PingResult{Status: "ok", PID: d.pid, WebURL: d.webURL})
|
info := buildinfo.Current()
|
||||||
|
result, _ := rpc.NewResult(api.PingResult{
|
||||||
|
Status: "ok",
|
||||||
|
PID: d.pid,
|
||||||
|
WebURL: d.webURL,
|
||||||
|
Version: info.Version,
|
||||||
|
Commit: info.Commit,
|
||||||
|
BuiltAt: info.BuiltAt,
|
||||||
|
})
|
||||||
return result
|
return result
|
||||||
case "shutdown":
|
case "shutdown":
|
||||||
go d.Close()
|
go d.Close()
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,17 @@ package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"banger/internal/api"
|
"banger/internal/api"
|
||||||
|
"banger/internal/buildinfo"
|
||||||
"banger/internal/model"
|
"banger/internal/model"
|
||||||
"banger/internal/paths"
|
"banger/internal/paths"
|
||||||
|
"banger/internal/rpc"
|
||||||
"banger/internal/system"
|
"banger/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,6 +45,28 @@ func TestRegisterImageRequiresKernel(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestPromoteImageCopiesBootArtifactsIntoArtifactDir(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
rootfs := filepath.Join(dir, "rootfs.ext4")
|
rootfs := filepath.Join(dir, "rootfs.ext4")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue