test: run control suite through compiled binaries
This commit is contained in:
@@ -15,6 +15,11 @@ services:
|
||||
GOFLAGS: -p=2
|
||||
HOME: /tmp
|
||||
XDG_CACHE_HOME: /cache/xdg
|
||||
tmpfs:
|
||||
# Integration binaries must exercise the production /run Unix-socket
|
||||
# policy without writing host runtime state as the unprivileged toolchain
|
||||
# user.
|
||||
- /run:uid=1001,gid=1001,mode=0700,size=8m
|
||||
volumes:
|
||||
- ..:/workspace
|
||||
- rvbox-go-build:/cache/go-build
|
||||
|
||||
+4
-1
@@ -45,7 +45,10 @@ tests = ["internal/server/control/service_test.go:TestCommandPaginationBindsFilt
|
||||
id = "HP-CTL-08"
|
||||
layer = "integration"
|
||||
status = "implemented"
|
||||
tests = ["internal/server/control/service_test.go:TestControlGRPCRoundTrip_HP_CONTROL_06"]
|
||||
tests = [
|
||||
"internal/server/control/service_test.go:TestControlGRPCRoundTrip_HP_CONTROL_06",
|
||||
"test/integration/controlplane/controlplane_integration_test.go:TestCompiledServerAndRVC_HP_CTL_08",
|
||||
]
|
||||
|
||||
[[requirements]]
|
||||
id = "HP-CTL-09"
|
||||
|
||||
@@ -355,7 +355,7 @@ func (h *harness) runIntegrationSuite(ctx context.Context, current *manifest, su
|
||||
case "server-session":
|
||||
return h.runServerSessionSuite(ctx, current, testCase)
|
||||
case "control":
|
||||
return h.runGoSuite(ctx, current, "control-real-grpc", "running gRPC control and JSON-RPC compatibility cases", "./internal/server/control", testCase)
|
||||
return h.runGoSuite(ctx, current, "control-compiled-server-rvc", "running compiled server/rvc control-plane boundary cases", "./test/integration/controlplane", testCase)
|
||||
case "client-agent":
|
||||
return h.runGoSuite(ctx, current, "client-agent-real-websocket", "running real client/server WebSocket control-flow cases", "./test/integration/clientagent", testCase)
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package controlplane_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestCompiledServerAndRVC_HP_CTL_08 proves the public Linux control boundary:
|
||||
// independently compiled binaries, a mode-0600 Unix socket, and a real gRPC
|
||||
// request. It deliberately does not import either command package.
|
||||
func TestCompiledServerAndRVC_HP_CTL_08(t *testing.T) {
|
||||
root := filepath.Clean(filepath.Join("..", "..", ".."))
|
||||
work := t.TempDir()
|
||||
serverBin := filepath.Join(work, "rvbox-server")
|
||||
rvcBin := filepath.Join(work, "rvc")
|
||||
build := func(output, target string) {
|
||||
t.Helper()
|
||||
command := exec.Command("go", "build", "-trimpath", "-o", output, target)
|
||||
command.Dir = root
|
||||
if data, err := command.CombinedOutput(); err != nil {
|
||||
t.Fatalf("build %s: %v\n%s", target, err, data)
|
||||
}
|
||||
}
|
||||
build(serverBin, "./cmd/rvbox-server")
|
||||
build(rvcBin, "./cmd/rvc")
|
||||
|
||||
agentAddress := reserveLoopbackAddress(t)
|
||||
healthAddress := reserveLoopbackAddress(t)
|
||||
runtime, err := os.MkdirTemp("/run", "rvbox-controlplane-")
|
||||
if err != nil {
|
||||
t.Fatalf("create runtime socket directory: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = os.RemoveAll(runtime) })
|
||||
socket := filepath.Join(runtime, "control.sock")
|
||||
config := filepath.Join(work, "server.toml")
|
||||
contents := fmt.Sprintf("[server]\ndata_dir = %q\nagent_listen = %q\ncontrol_socket = %q\n\n[observability]\nlisten = %q\n", filepath.Join(work, "state"), agentAddress, socket, healthAddress)
|
||||
if err := os.WriteFile(config, []byte(contents), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var serverLog bytes.Buffer
|
||||
server := exec.Command(serverBin, "--config", config)
|
||||
server.Stdout = &serverLog
|
||||
server.Stderr = &serverLog
|
||||
if err := server.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if server.Process != nil {
|
||||
_ = server.Process.Signal(os.Interrupt)
|
||||
}
|
||||
_ = server.Wait()
|
||||
})
|
||||
deadline := time.Now().Add(10 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
info, err := os.Stat(socket)
|
||||
if err == nil {
|
||||
if info.Mode().Perm() != 0o600 {
|
||||
t.Fatalf("control socket permissions = %o, want 0600", info.Mode().Perm())
|
||||
}
|
||||
break
|
||||
}
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
}
|
||||
if _, err := os.Stat(socket); err != nil {
|
||||
t.Fatalf("server did not create control socket: %v\n%s", err, serverLog.String())
|
||||
}
|
||||
command := exec.Command(rvcBin, "--socket", socket, "stat")
|
||||
if data, err := command.CombinedOutput(); err != nil {
|
||||
t.Fatalf("rvc stat: %v\n%s\nserver:\n%s", err, data, serverLog.String())
|
||||
}
|
||||
}
|
||||
|
||||
func reserveLoopbackAddress(t *testing.T) string {
|
||||
t.Helper()
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
address := listener.Addr().String()
|
||||
if err := listener.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return address
|
||||
}
|
||||
Reference in New Issue
Block a user