panel v0.9.2 — public release

Self-hostable game server control panel: Go controller + agent, 26 game
modules, embedded web UI. One-line install via install.sh / install.ps1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 00:43:35 -07:00
commit 4ccccc6fe2
2164 changed files with 301480 additions and 0 deletions
@@ -0,0 +1,67 @@
package runtime
import (
"os"
"testing"
)
// TestDetectHostNetUnsupported_EnvOverrides verifies the explicit env
// overrides win over autodetection (the daemon-info path needs a live
// daemon and is covered by the integration smoke test, not here).
func TestDetectHostNetUnsupported_EnvOverrides(t *testing.T) {
t.Setenv("PANEL_FORCE_BRIDGE", "1")
if !detectHostNetUnsupported(nil) {
t.Fatal("PANEL_FORCE_BRIDGE=1 must force bridge fallback (true)")
}
os.Unsetenv("PANEL_FORCE_BRIDGE")
t.Setenv("PANEL_FORCE_HOST_NET", "1")
if detectHostNetUnsupported(nil) {
t.Fatal("PANEL_FORCE_HOST_NET=1 must force host networking (false)")
}
}
// TestBridgeFallbackPreservesBindings is the load-bearing check: on a
// Docker-Desktop-style daemon, a host-mode module must be converted to
// bridge networking WITH its published port bindings intact (so the
// panel-allocated ports actually reach players), whereas on native Linux
// the same module keeps host mode and drops the meaningless bindings.
//
// It exercises the exact decision block in Create() without a live daemon
// by replicating its inputs.
func TestBridgeFallbackPreservesBindings(t *testing.T) {
ports := []PortSpec{
{Proto: "udp", HostPort: 14507, ContainerPort: 14507},
{Proto: "udp", HostPort: 14508, ContainerPort: 14508},
}
exposed, bindings, err := buildPortBindings(ports)
if err != nil {
t.Fatalf("buildPortBindings: %v", err)
}
if len(bindings) != 2 {
t.Fatalf("expected 2 published bindings, got %d", len(bindings))
}
// Native Linux (hostNetUnsupported=false): host mode kept, bindings dropped.
r := &DockerRuntime{hostNetUnsupported: false}
netMode, gotExposed, gotBindings := applyHostNetPolicy(r, "host", exposed, bindings)
if netMode != "host" {
t.Errorf("linux: netMode = %q, want host", netMode)
}
if gotBindings != nil || gotExposed != nil {
t.Errorf("linux: expected bindings/exposed dropped for host mode")
}
// Docker Desktop (hostNetUnsupported=true): host mode → bridge, bindings kept.
r = &DockerRuntime{hostNetUnsupported: true}
netMode, gotExposed, gotBindings = applyHostNetPolicy(r, "host", exposed, bindings)
if netMode != "" {
t.Errorf("docker desktop: netMode = %q, want bridge (empty)", netMode)
}
if len(gotBindings) != 2 {
t.Errorf("docker desktop: expected 2 bindings preserved, got %d", len(gotBindings))
}
if len(gotExposed) != 2 {
t.Errorf("docker desktop: expected 2 exposed ports preserved, got %d", len(gotExposed))
}
}