test: run control suite through compiled binaries
This commit is contained in:
@@ -15,6 +15,11 @@ services:
|
|||||||
GOFLAGS: -p=2
|
GOFLAGS: -p=2
|
||||||
HOME: /tmp
|
HOME: /tmp
|
||||||
XDG_CACHE_HOME: /cache/xdg
|
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:
|
volumes:
|
||||||
- ..:/workspace
|
- ..:/workspace
|
||||||
- rvbox-go-build:/cache/go-build
|
- rvbox-go-build:/cache/go-build
|
||||||
|
|||||||
+4
-1
@@ -45,7 +45,10 @@ tests = ["internal/server/control/service_test.go:TestCommandPaginationBindsFilt
|
|||||||
id = "HP-CTL-08"
|
id = "HP-CTL-08"
|
||||||
layer = "integration"
|
layer = "integration"
|
||||||
status = "implemented"
|
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]]
|
[[requirements]]
|
||||||
id = "HP-CTL-09"
|
id = "HP-CTL-09"
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ func (h *harness) runIntegrationSuite(ctx context.Context, current *manifest, su
|
|||||||
case "server-session":
|
case "server-session":
|
||||||
return h.runServerSessionSuite(ctx, current, testCase)
|
return h.runServerSessionSuite(ctx, current, testCase)
|
||||||
case "control":
|
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":
|
case "client-agent":
|
||||||
return h.runGoSuite(ctx, current, "client-agent-real-websocket", "running real client/server WebSocket control-flow cases", "./test/integration/clientagent", testCase)
|
return h.runGoSuite(ctx, current, "client-agent-real-websocket", "running real client/server WebSocket control-flow cases", "./test/integration/clientagent", testCase)
|
||||||
default:
|
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