This commit is contained in:
2026-05-18 21:33:39 +03:00
parent 5096155d83
commit 469fa0e860
94 changed files with 8761 additions and 8003 deletions
@@ -140,15 +140,12 @@ func run(ctx context.Context) (smokeReport, error) {
return smokeReport{}, fmt.Errorf("test service: %w", err)
}
fabricSessionStartedAt := time.Now()
fabricSession, _, err := mesh.NewClient(nodeB.URL).OpenFabricSession(ctx, mesh.FabricSessionDialOptions{
Token: "rap_fsn_mesh_live_smoke",
Timeout: 3 * time.Second,
})
fabricSession, fabricQUICEndpoint, fabricQUICPressure, err := smokeQUICFabricSession(ctx)
if err != nil {
return smokeReport{}, fmt.Errorf("fabric session open: %w", err)
return smokeReport{}, fmt.Errorf("fabric quic session open: %w", err)
}
defer fabricSession.Close()
firstFabricSessionResponse, err := fabricSession.RoundTrip(ctx, fabricproto.Frame{
firstFabricSessionResponse, err := smokeFabricSessionRoundTrip(ctx, fabricSession, fabricproto.Frame{
Type: fabricproto.FramePing,
Sequence: uint64(fabricSessionStartedAt.UnixNano()),
Payload: []byte("mesh-live-smoke-fabric-session"),
@@ -156,7 +153,7 @@ func run(ctx context.Context) (smokeReport, error) {
if err != nil {
return smokeReport{}, fmt.Errorf("fabric session first round trip: %w", err)
}
secondFabricSessionResponse, err := fabricSession.RoundTrip(ctx, fabricproto.Frame{
secondFabricSessionResponse, err := smokeFabricSessionRoundTrip(ctx, fabricSession, fabricproto.Frame{
Type: fabricproto.FramePing,
Sequence: uint64(fabricSessionStartedAt.UnixNano()) + 1,
Payload: []byte("mesh-live-smoke-fabric-session-2"),
@@ -175,13 +172,9 @@ func run(ctx context.Context) (smokeReport, error) {
}
fabricVPNBulkPressure, fabricVPNBulkChannels, fabricVPNInteractiveChannels, fabricVPNBulkWindow, fabricVPNInteractiveWindow, fabricVPNPressureLevel, fabricVPNPressureScore, fabricVPNPressureReasons, fabricVPNPressureAction := smokeVPNFlowSchedulerBulkPressure()
fabricVPNRouteRecovered, fabricVPNRouteSwitches, fabricVPNRecoveryMS, fabricVPNRecoveryMaxMS, fabricVPNRecoveryAvgMS, fabricVPNRecoveryReason := smokeVPNFlowSchedulerRouteRecovery()
fabricQUICAccepted, fabricQUICEndpoint, fabricQUICPressure, err := smokeQUICFabricSession(ctx)
if err != nil {
return smokeReport{}, fmt.Errorf("fabric quic smoke: %w", err)
}
return smokeReport{
Stage: "C17F scoped synthetic config plus live HTTP transport",
Stage: "C17F scoped synthetic config plus live QUIC fabric transport",
ProductionForwarding: false,
ScopedConfigLoaded: nodeAConfig.ConfigVersion == "smoke-config-v1",
DirectProbeAccepted: directAck.MessageType == mesh.SyntheticMessageProbeAck,
@@ -210,11 +203,11 @@ func run(ctx context.Context) (smokeReport, error) {
FabricVPNRecoveryMaxMS: fabricVPNRecoveryMaxMS,
FabricVPNRecoveryAvgMS: fabricVPNRecoveryAvgMS,
FabricVPNRecoveryReason: fabricVPNRecoveryReason,
FabricQUICAccepted: fabricQUICAccepted,
FabricQUICAccepted: fabricSessionAccepted,
FabricQUICEndpoint: fabricQUICEndpoint,
FabricQUICPressure: fabricQUICPressure,
FabricSessionLatencyMS: fabricSessionLatency.Milliseconds(),
FabricSessionEndpoint: nodeB.URL + "/mesh/v1/fabric/session/ws",
FabricSessionEndpoint: "quic://" + fabricQUICEndpoint,
PeerEndpoints: map[string]any{
"node-a": nodeA.URL,
"node-r": nodeR.URL,
@@ -269,18 +262,16 @@ func smokeVPNFlowSchedulerRouteRecovery() (bool, uint64, int64, int64, int64, st
stat.LastRouteSwitchReason
}
func smokeQUICFabricSession(ctx context.Context) (bool, string, int, error) {
func smokeQUICFabricSession(ctx context.Context) (mesh.FabricTransportSession, string, int, error) {
server, err := mesh.StartQUICFabricServer(ctx, mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: smokeQUICTLSConfig(),
})
if err != nil {
return false, "", 0, err
return nil, "", 0, err
}
defer server.Close()
endpoint := server.Addr().String()
transport := mesh.NewQUICFabricTransport(nil)
defer transport.Close()
session, err := transport.Connect(ctx, mesh.FabricTransportTarget{
PeerID: "node-b",
Endpoint: endpoint,
@@ -293,31 +284,12 @@ func smokeQUICFabricSession(ctx context.Context) (bool, string, int, error) {
ErrorBuffer: 4,
})
if err != nil {
return false, endpoint, 0, err
}
defer session.Close()
if err := session.Send(ctx, fabricproto.Frame{
Type: fabricproto.FramePing,
Sequence: uint64(time.Now().UnixNano()),
Payload: []byte("mesh-live-smoke-quic"),
}); err != nil {
return false, endpoint, 0, err
}
timer := time.NewTimer(3 * time.Second)
defer timer.Stop()
for {
select {
case frame := <-session.Frames():
snapshot := transport.Snapshot()
return frame.Type == fabricproto.FramePong && string(frame.Payload) == "mesh-live-smoke-quic", endpoint, snapshot.CapacityPressurePercent, nil
case err := <-session.Errors():
return false, endpoint, 0, err
case <-timer.C:
return false, endpoint, 0, fmt.Errorf("timed out waiting for quic pong")
case <-ctx.Done():
return false, endpoint, 0, ctx.Err()
}
_ = transport.Close()
_ = server.Close()
return nil, endpoint, 0, err
}
snapshot := transport.Snapshot()
return &smokeManagedFabricSession{session: session, transport: transport, server: server}, endpoint, snapshot.CapacityPressurePercent, nil
}
func smokeQUICTLSConfig() *tls.Config {
@@ -341,25 +313,20 @@ func smokeQUICTLSConfig() *tls.Config {
}
}
func smokeFabricVPNPacketOverSession(ctx context.Context, fabricSession *mesh.FabricSessionClient) (bool, bool, int, error) {
func smokeFabricVPNPacketOverSession(ctx context.Context, fabricSession mesh.FabricTransportSession) (bool, bool, int, error) {
const interactiveStreamID uint64 = 4400
const bulkStreamID uint64 = 4401
pump := fabricSession.StartPump(ctx, mesh.FabricSessionPumpOptions{
OutboundBuffer: 4,
InboundBuffer: 4,
ErrorBuffer: 4,
})
defer pump.Close()
for _, frame := range []fabricproto.Frame{
{Type: fabricproto.FrameOpenStream, StreamID: interactiveStreamID, TrafficClass: fabricproto.TrafficClassInteractive},
{Type: fabricproto.FrameOpenStream, StreamID: bulkStreamID, TrafficClass: fabricproto.TrafficClassBulk},
} {
if err := pump.Send(ctx, frame); err != nil {
if err := fabricSession.Send(ctx, frame); err != nil {
return false, false, 0, err
}
}
transport := &vpnruntime.FabricSessionPacketTransport{
Sender: pump,
Sender: fabricSession,
Receiver: fabricSession,
StreamID: interactiveStreamID,
VPNConnectionID: "vpn-smoke",
SendDirection: vpnruntime.FabricDirectionGatewayToClient,
@@ -378,7 +345,7 @@ func smokeFabricVPNPacketOverSession(ctx context.Context, fabricSession *mesh.Fa
acked := map[uint64]bool{}
for {
select {
case frame := <-pump.Frames():
case frame := <-fabricSession.Frames():
if frame.Type == fabricproto.FrameAck && frame.Sequence == 1 {
acked[frame.StreamID] = true
if acked[interactiveStreamID] && acked[bulkStreamID] {
@@ -393,7 +360,7 @@ func smokeFabricVPNPacketOverSession(ctx context.Context, fabricSession *mesh.Fa
return true, sharded, int(fanout), nil
}
}
case err := <-pump.Errors():
case err := <-fabricSession.Errors():
return false, false, 0, err
case <-timer.C:
return false, false, 0, fmt.Errorf("timed out waiting for fabric vpn packet ack")
@@ -403,6 +370,68 @@ func smokeFabricVPNPacketOverSession(ctx context.Context, fabricSession *mesh.Fa
}
}
type smokeManagedFabricSession struct {
session mesh.FabricTransportSession
transport *mesh.QUICFabricTransport
server *mesh.QUICFabricServer
}
func (s *smokeManagedFabricSession) Send(ctx context.Context, frame fabricproto.Frame) error {
return s.session.Send(ctx, frame)
}
func (s *smokeManagedFabricSession) Frames() <-chan fabricproto.Frame {
return s.session.Frames()
}
func (s *smokeManagedFabricSession) Errors() <-chan error {
return s.session.Errors()
}
func (s *smokeManagedFabricSession) Closed() bool {
return s.session.Closed()
}
func (s *smokeManagedFabricSession) Close() error {
var firstErr error
if s.session != nil {
firstErr = s.session.Close()
}
if s.transport != nil {
if err := s.transport.Close(); firstErr == nil {
firstErr = err
}
}
if s.server != nil {
if err := s.server.Close(); firstErr == nil {
firstErr = err
}
}
return firstErr
}
func smokeFabricSessionRoundTrip(ctx context.Context, session mesh.FabricTransportSession, frame fabricproto.Frame) (fabricproto.Frame, error) {
if err := session.Send(ctx, frame); err != nil {
return fabricproto.Frame{}, err
}
timer := time.NewTimer(3 * time.Second)
defer timer.Stop()
for {
select {
case response := <-session.Frames():
if response.Sequence == frame.Sequence {
return response, nil
}
case err := <-session.Errors():
return fabricproto.Frame{}, err
case <-timer.C:
return fabricproto.Frame{}, fmt.Errorf("timed out waiting for fabric session response")
case <-ctx.Done():
return fabricproto.Frame{}, ctx.Err()
}
}
}
func smokeIPv4TCPPacket(src [4]byte, dst [4]byte, srcPort uint16, dstPort uint16, flags byte) []byte {
packet := make([]byte, 40)
packet[0] = 0x45
@@ -445,7 +474,7 @@ func writeSmokeScopedConfig(local mesh.PeerIdentity, peers map[string]string, ro
func newSmokeNode(local mesh.PeerIdentity) *smokeNode {
node := &smokeNode{Local: local}
node.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mesh.Server{Local: node.Local, SyntheticRuntime: node.Runtime, FabricSessionEnabled: true, FabricSessionWebSocketEnabled: true}.Handler().ServeHTTP(w, r)
mesh.Server{Local: node.Local, SyntheticRuntime: node.Runtime}.Handler().ServeHTTP(w, r)
}))
node.URL = node.server.URL
return node
+35 -106
View File
@@ -6,7 +6,6 @@ import (
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"runtime"
@@ -15,9 +14,7 @@ import (
"time"
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/agent"
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/fabricproto"
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/hostagent"
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/mesh"
)
type installCommandConfig struct {
@@ -82,10 +79,6 @@ func main() {
if err := runUpdateHostAgentLoop(ctx, os.Args[2:]); err != nil {
log.Fatalf("update-host-agent-loop failed: %v", err)
}
case "fabric-session-smoke":
if err := runFabricSessionSmoke(ctx, os.Args[2:]); err != nil {
log.Fatalf("fabric-session-smoke failed: %v", err)
}
default:
usage()
os.Exit(2)
@@ -117,78 +110,6 @@ func applyStagedSelfUpdate() {
_ = os.Remove(backup)
}
func runFabricSessionSmoke(ctx context.Context, args []string) error {
fs := flag.NewFlagSet("fabric-session-smoke", flag.ContinueOnError)
var meshURL string
var token string
var timeoutSeconds int
var payload string
var authorityPayload string
var authoritySignature string
fs.StringVar(&meshURL, "mesh-url", getenv("RAP_MESH_SMOKE_URL", ""), "Mesh base URL, for example http://node:19131.")
fs.StringVar(&token, "token", getenv("RAP_FABRIC_SESSION_TOKEN", ""), "Fabric session token starting with rap_fsn_.")
fs.IntVar(&timeoutSeconds, "timeout-seconds", getenvInt("RAP_FABRIC_SESSION_SMOKE_TIMEOUT_SECONDS", 5), "Smoke timeout in seconds.")
fs.StringVar(&payload, "payload", getenv("RAP_FABRIC_SESSION_SMOKE_PAYLOAD", "rap-fabric-session-smoke"), "Ping payload.")
fs.StringVar(&authorityPayload, "authority-payload", getenv("RAP_FABRIC_SESSION_AUTHORITY_PAYLOAD", ""), "Base64 or JSON fabric session authority payload header.")
fs.StringVar(&authoritySignature, "authority-signature", getenv("RAP_FABRIC_SESSION_AUTHORITY_SIGNATURE", ""), "Base64 or JSON fabric session authority signature header.")
if err := fs.Parse(args); err != nil {
return err
}
if strings.TrimSpace(meshURL) == "" {
return fmt.Errorf("mesh-url is required")
}
if strings.TrimSpace(token) == "" {
return fmt.Errorf("token is required")
}
if timeoutSeconds <= 0 {
timeoutSeconds = 5
}
smokeCtx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSeconds)*time.Second)
defer cancel()
header := make(http.Header)
if strings.TrimSpace(authorityPayload) != "" {
header.Set("X-RAP-Fabric-Session-Authority-Payload", strings.TrimSpace(authorityPayload))
}
if strings.TrimSpace(authoritySignature) != "" {
header.Set("X-RAP-Fabric-Session-Authority-Signature", strings.TrimSpace(authoritySignature))
}
startedAt := time.Now()
response, err := mesh.NewClient(meshURL).SendFabricSessionFrame(smokeCtx, mesh.FabricSessionDialOptions{
Token: token,
Header: header,
Timeout: time.Duration(timeoutSeconds) * time.Second,
}, fabricproto.Frame{
Type: fabricproto.FramePing,
Sequence: uint64(startedAt.UnixNano()),
Payload: []byte(payload),
})
duration := time.Since(startedAt)
result := map[string]any{
"schema_version": "rap.fabric_session_smoke_result.v1",
"mesh_url": strings.TrimSpace(meshURL),
"ok": err == nil && response.Type == fabricproto.FramePong && string(response.Payload) == payload,
"latency_ms": duration.Milliseconds(),
"response_type": response.Type,
"sequence": response.Sequence,
"authority": strings.TrimSpace(authorityPayload) != "" || strings.TrimSpace(authoritySignature) != "",
}
if err != nil {
result["error"] = err.Error()
}
encoded, marshalErr := json.MarshalIndent(result, "", " ")
if marshalErr != nil {
return marshalErr
}
fmt.Println(string(encoded))
if err != nil {
return err
}
if response.Type != fabricproto.FramePong || string(response.Payload) != payload {
return fmt.Errorf("fabric session smoke returned unexpected response type=%d payload=%q", response.Type, string(response.Payload))
}
return nil
}
func runInstallLinux(ctx context.Context, args []string) error {
fs := flag.NewFlagSet("install-linux", flag.ContinueOnError)
cfg := hostagent.LinuxInstallConfig{}
@@ -215,16 +136,15 @@ func runInstallLinux(ctx context.Context, args []string) error {
fs.IntVar(&cfg.AutoUpdateHealthTimeoutSeconds, "auto-update-health-timeout-seconds", getenvInt("RAP_UPDATE_HEALTH_TIMEOUT_SECONDS", 30), "Updated service health timeout in seconds.")
fs.StringVar(&cfg.HostAgentSourcePath, "host-agent-source-path", getenv("RAP_HOST_AGENT_SOURCE_PATH", ""), "Source rap-host-agent path copied to the persistent updater location.")
fs.BoolVar(&cfg.RuntimeConfig.WorkloadSupervisionEnabled, "workload-supervision-enabled", getenvBool("RAP_WORKLOAD_SUPERVISION_ENABLED", false), "Enable node-agent workload status reporting.")
fs.BoolVar(&cfg.RuntimeConfig.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", true), "Enable synthetic mesh runtime.")
fs.BoolVar(&cfg.RuntimeConfig.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", false), "Enable historical synthetic mesh runtime.")
fs.BoolVar(&cfg.RuntimeConfig.MeshProductionForwardingEnabled, "mesh-production-forwarding-enabled", getenvBool("RAP_MESH_PRODUCTION_FORWARDING_ENABLED", false), "Enable production forwarding gate; runtime still fail-closed if unavailable.")
fs.BoolVar(&cfg.RuntimeConfig.MeshFabricSessionEnabled, "mesh-fabric-session-enabled", getenvBool("RAP_MESH_FABRIC_SESSION_ENABLED", false), "Enable authenticated fabric session endpoint.")
fs.BoolVar(&cfg.RuntimeConfig.VPNFabricSessionTransportEnabled, "vpn-fabric-session-transport-enabled", getenvBool("RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED", false), "Route VPN packet transport over persistent fabric sessions.")
fs.BoolVar(&cfg.RuntimeConfig.MeshQUICFabricEnabled, "mesh-quic-fabric-enabled", getenvBool("RAP_MESH_QUIC_FABRIC_ENABLED", false), "Enable QUIC/UDP fabric listener.")
fs.StringVar(&cfg.RuntimeConfig.MeshQUICFabricListenAddr, "mesh-quic-fabric-listen-addr", getenv("RAP_MESH_QUIC_FABRIC_LISTEN_ADDR", ""), "QUIC/UDP fabric listen address.")
fs.IntVar(&cfg.RuntimeConfig.VPNFabricSessionStreamShards, "vpn-fabric-session-stream-shards", getenvInt("RAP_VPN_FABRIC_SESSION_STREAM_SHARDS", 4), "VPN fabric-session stream shards per traffic class.")
fs.IntVar(&cfg.RuntimeConfig.VPNFabricQUICMaxStreamsPerConn, "vpn-fabric-quic-max-streams-per-conn", getenvInt("RAP_VPN_FABRIC_QUIC_MAX_STREAMS_PER_CONN", 64), "Maximum logical fabric-session streams per cached VPN QUIC carrier connection.")
fs.IntVar(&cfg.RuntimeConfig.VPNFabricQUICIdleTTLSeconds, "vpn-fabric-quic-idle-ttl-seconds", getenvInt("RAP_VPN_FABRIC_QUIC_IDLE_TTL_SECONDS", 300), "Idle TTL seconds for cached VPN QUIC carrier connections.")
fs.StringVar(&cfg.RuntimeConfig.MeshListenAddr, "mesh-listen-addr", getenv("RAP_MESH_LISTEN_ADDR", ":19131"), "Synthetic mesh HTTP listen address.")
fs.StringVar(&cfg.RuntimeConfig.MeshListenAddr, "mesh-listen-addr", getenv("RAP_MESH_LISTEN_ADDR", ""), "Historical synthetic mesh HTTP listen address.")
fs.StringVar(&cfg.RuntimeConfig.MeshListenPortMode, "mesh-listen-port-mode", getenv("RAP_MESH_LISTEN_PORT_MODE", "auto"), "Mesh listen port behavior: manual, auto, or disabled.")
fs.IntVar(&cfg.RuntimeConfig.MeshListenAutoPortStart, "mesh-listen-auto-port-start", getenvInt("RAP_MESH_LISTEN_AUTO_PORT_START", 19131), "First port used when mesh listen port mode is auto.")
fs.IntVar(&cfg.RuntimeConfig.MeshListenAutoPortEnd, "mesh-listen-auto-port-end", getenvInt("RAP_MESH_LISTEN_AUTO_PORT_END", 19231), "Last port used when mesh listen port mode is auto.")
@@ -303,16 +223,15 @@ func runInstallWindows(ctx context.Context, args []string) error {
fs.IntVar(&cfg.AutoUpdateHealthTimeoutSeconds, "auto-update-health-timeout-seconds", getenvInt("RAP_UPDATE_HEALTH_TIMEOUT_SECONDS", 30), "Updated service health timeout in seconds.")
fs.StringVar(&cfg.HostAgentSourcePath, "host-agent-source-path", getenv("RAP_HOST_AGENT_SOURCE_PATH", ""), "Source rap-host-agent.exe path copied to the persistent updater location.")
fs.BoolVar(&cfg.RuntimeConfig.WorkloadSupervisionEnabled, "workload-supervision-enabled", getenvBool("RAP_WORKLOAD_SUPERVISION_ENABLED", false), "Enable node-agent workload status reporting.")
fs.BoolVar(&cfg.RuntimeConfig.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", true), "Enable synthetic mesh runtime.")
fs.BoolVar(&cfg.RuntimeConfig.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", false), "Enable historical synthetic mesh runtime.")
fs.BoolVar(&cfg.RuntimeConfig.MeshProductionForwardingEnabled, "mesh-production-forwarding-enabled", getenvBool("RAP_MESH_PRODUCTION_FORWARDING_ENABLED", false), "Enable production forwarding gate; runtime still fail-closed if unavailable.")
fs.BoolVar(&cfg.RuntimeConfig.MeshFabricSessionEnabled, "mesh-fabric-session-enabled", getenvBool("RAP_MESH_FABRIC_SESSION_ENABLED", false), "Enable authenticated fabric session endpoint.")
fs.BoolVar(&cfg.RuntimeConfig.VPNFabricSessionTransportEnabled, "vpn-fabric-session-transport-enabled", getenvBool("RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED", false), "Route VPN packet transport over persistent fabric sessions.")
fs.BoolVar(&cfg.RuntimeConfig.MeshQUICFabricEnabled, "mesh-quic-fabric-enabled", getenvBool("RAP_MESH_QUIC_FABRIC_ENABLED", false), "Enable QUIC/UDP fabric listener.")
fs.StringVar(&cfg.RuntimeConfig.MeshQUICFabricListenAddr, "mesh-quic-fabric-listen-addr", getenv("RAP_MESH_QUIC_FABRIC_LISTEN_ADDR", ""), "QUIC/UDP fabric listen address.")
fs.IntVar(&cfg.RuntimeConfig.VPNFabricSessionStreamShards, "vpn-fabric-session-stream-shards", getenvInt("RAP_VPN_FABRIC_SESSION_STREAM_SHARDS", 4), "VPN fabric-session stream shards per traffic class.")
fs.IntVar(&cfg.RuntimeConfig.VPNFabricQUICMaxStreamsPerConn, "vpn-fabric-quic-max-streams-per-conn", getenvInt("RAP_VPN_FABRIC_QUIC_MAX_STREAMS_PER_CONN", 64), "Maximum logical fabric-session streams per cached VPN QUIC carrier connection.")
fs.IntVar(&cfg.RuntimeConfig.VPNFabricQUICIdleTTLSeconds, "vpn-fabric-quic-idle-ttl-seconds", getenvInt("RAP_VPN_FABRIC_QUIC_IDLE_TTL_SECONDS", 300), "Idle TTL seconds for cached VPN QUIC carrier connections.")
fs.StringVar(&cfg.RuntimeConfig.MeshListenAddr, "mesh-listen-addr", getenv("RAP_MESH_LISTEN_ADDR", ":19131"), "Synthetic mesh HTTP listen address.")
fs.StringVar(&cfg.RuntimeConfig.MeshListenAddr, "mesh-listen-addr", getenv("RAP_MESH_LISTEN_ADDR", ""), "Historical synthetic mesh HTTP listen address.")
fs.StringVar(&cfg.RuntimeConfig.MeshListenPortMode, "mesh-listen-port-mode", getenv("RAP_MESH_LISTEN_PORT_MODE", "auto"), "Mesh listen port behavior: manual, auto, or disabled.")
fs.IntVar(&cfg.RuntimeConfig.MeshListenAutoPortStart, "mesh-listen-auto-port-start", getenvInt("RAP_MESH_LISTEN_AUTO_PORT_START", 19131), "First port used when mesh listen port mode is auto.")
fs.IntVar(&cfg.RuntimeConfig.MeshListenAutoPortEnd, "mesh-listen-auto-port-end", getenvInt("RAP_MESH_LISTEN_AUTO_PORT_END", 19231), "Last port used when mesh listen port mode is auto.")
@@ -513,16 +432,19 @@ func runUpdateLoop(ctx context.Context, args []string) error {
}
cfg.HostAgentUpdateEnabled = hostAgentStatusEnabled
cfg.HostAgentUpdateRequest = hostagent.HostAgentUpdateRequest{
BackendURL: req.BackendURL,
ClusterID: req.ClusterID,
NodeID: req.NodeID,
StateDir: req.StateDir,
CurrentVersion: hostAgentVersion,
Channel: req.Channel,
OS: firstNonEmptyLocal(req.OS, runtime.GOOS),
Arch: firstNonEmptyLocal(req.Arch, runtime.GOARCH),
InstallType: hostagent.BinaryUpdateInstallType,
BinaryPath: hostAgentBinaryPath,
BackendURL: req.BackendURL,
ClusterID: req.ClusterID,
NodeID: req.NodeID,
StateDir: req.StateDir,
ClusterAuthorityPublicKey: req.ClusterAuthorityPublicKey,
FabricRegistryRecordsJSON: req.FabricRegistryRecordsJSON,
MeshRegion: req.MeshRegion,
CurrentVersion: hostAgentVersion,
Channel: req.Channel,
OS: firstNonEmptyLocal(req.OS, runtime.GOOS),
Arch: firstNonEmptyLocal(req.Arch, runtime.GOARCH),
InstallType: hostagent.BinaryUpdateInstallType,
BinaryPath: hostAgentBinaryPath,
}
if req.InstallType == hostagent.WindowsUpdateInstallType || runtime.GOOS == "windows" {
cfg.HostAgentUpdateRequest.InstallType = "windows_binary"
@@ -569,6 +491,9 @@ func parseMonitor(args []string) (hostagent.MonitorConfig, error) {
fs.StringVar(&cfg.ClusterID, "cluster-id", getenv("RAP_CLUSTER_ID", ""), "Cluster ID.")
fs.StringVar(&cfg.NodeID, "node-id", getenv("RAP_NODE_ID", ""), "Already enrolled node ID.")
fs.StringVar(&cfg.StateDir, "state-dir", getenv("RAP_NODE_STATE_DIR", hostagent.DefaultStateDir), "Host path containing node-agent identity.json.")
fs.StringVar(&cfg.ClusterAuthorityPublicKey, "cluster-authority-public-key", getenv("RAP_CLUSTER_AUTHORITY_PUBLIC_KEY", ""), "Pinned Ed25519 cluster authority public key for signed fabric registry records.")
fs.StringVar(&cfg.FabricRegistryRecordsJSON, "fabric-registry-records-json", getenv("RAP_FABRIC_REGISTRY_RECORDS_JSON", ""), "JSON array of signed QUIC-only fabric registry records used to reach update/control services.")
fs.StringVar(&cfg.MeshRegion, "mesh-region", getenv("RAP_MESH_REGION", ""), "Region/site hint for fabric registry endpoint selection.")
fs.StringVar(&cfg.Product, "product", getenv("RAP_MONITOR_PRODUCT", hostagent.DefaultMonitorProduct), "Status product name.")
fs.StringVar(&cfg.CurrentVersion, "current-version", getenv("RAP_HOST_AGENT_VERSION", agent.Version), "Current rap-host-agent version.")
fs.StringVar(&cfg.DockerBinary, "docker-binary", getenv("RAP_DOCKER_BINARY", "docker"), "Docker CLI binary.")
@@ -716,6 +641,9 @@ func parseHostAgentUpdate(args []string) (hostagent.HostAgentUpdateRequest, int,
fs.StringVar(&req.ClusterID, "cluster-id", getenv("RAP_CLUSTER_ID", ""), "Cluster ID.")
fs.StringVar(&req.NodeID, "node-id", getenv("RAP_NODE_ID", ""), "Already enrolled node ID.")
fs.StringVar(&req.StateDir, "state-dir", getenv("RAP_NODE_STATE_DIR", ""), "Host path containing node-agent identity.json.")
fs.StringVar(&req.ClusterAuthorityPublicKey, "cluster-authority-public-key", getenv("RAP_CLUSTER_AUTHORITY_PUBLIC_KEY", ""), "Pinned Ed25519 cluster authority public key for signed fabric registry records.")
fs.StringVar(&req.FabricRegistryRecordsJSON, "fabric-registry-records-json", getenv("RAP_FABRIC_REGISTRY_RECORDS_JSON", ""), "JSON array of signed QUIC-only fabric registry records used to reach update/control services.")
fs.StringVar(&req.MeshRegion, "mesh-region", getenv("RAP_MESH_REGION", ""), "Region/site hint for fabric registry endpoint selection.")
fs.StringVar(&req.CurrentVersion, "current-version", getenv("RAP_HOST_AGENT_VERSION", agent.Version), "Currently installed rap-host-agent version.")
fs.StringVar(&req.Channel, "channel", getenv("RAP_UPDATE_CHANNEL", ""), "Optional update channel override.")
fs.StringVar(&req.OS, "os", getenv("RAP_HOST_AGENT_UPDATE_OS", runtime.GOOS), "Host-agent artifact OS selector.")
@@ -739,6 +667,9 @@ func registerUpdateFlags(fs *flag.FlagSet, req *hostagent.UpdateRequest, healthT
fs.StringVar(&req.ClusterID, "cluster-id", getenv("RAP_CLUSTER_ID", ""), "Cluster ID.")
fs.StringVar(&req.NodeID, "node-id", getenv("RAP_NODE_ID", ""), "Already enrolled node ID.")
fs.StringVar(&req.StateDir, "state-dir", getenv("RAP_NODE_STATE_DIR", ""), "Host path containing node-agent identity.json; used when node-id is not known yet.")
fs.StringVar(&req.ClusterAuthorityPublicKey, "cluster-authority-public-key", getenv("RAP_CLUSTER_AUTHORITY_PUBLIC_KEY", ""), "Pinned Ed25519 cluster authority public key for signed fabric registry records.")
fs.StringVar(&req.FabricRegistryRecordsJSON, "fabric-registry-records-json", getenv("RAP_FABRIC_REGISTRY_RECORDS_JSON", ""), "JSON array of signed QUIC-only fabric registry records used to reach update/control services.")
fs.StringVar(&req.MeshRegion, "mesh-region", getenv("RAP_MESH_REGION", ""), "Region/site hint for fabric registry endpoint selection.")
fs.StringVar(&req.Product, "product", getenv("RAP_UPDATE_PRODUCT", hostagent.DefaultUpdateProduct), "Update product name.")
fs.StringVar(&req.CurrentVersion, "current-version", getenv("RAP_NODE_AGENT_VERSION", agent.Version), "Currently running product version.")
fs.StringVar(&req.OS, "os", getenv("RAP_UPDATE_OS", runtime.GOOS), "Artifact OS selector.")
@@ -797,16 +728,15 @@ func parseInstall(args []string) (installCommandConfig, error) {
fs.IntVar(&autoUpdate.MonitorDiskCritical, "monitor-disk-critical-percent", getenvInt("RAP_MONITOR_DISK_CRITICAL_PERCENT", hostagent.DefaultMonitorDiskCriticalPercent), "Disk used percent that reports failure after cleanup.")
fs.BoolVar(&autoUpdate.MonitorCleanupDocker, "monitor-cleanup-docker", getenvBool("RAP_MONITOR_CLEANUP_DOCKER", true), "Run safe docker prune cleanup when disk is above cleanup threshold.")
fs.BoolVar(&cfg.WorkloadSupervisionEnabled, "workload-supervision-enabled", getenvBool("RAP_WORKLOAD_SUPERVISION_ENABLED", false), "Enable node-agent workload status reporting.")
fs.BoolVar(&cfg.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", false), "Enable synthetic mesh runtime.")
fs.BoolVar(&cfg.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", false), "Enable historical synthetic mesh runtime.")
fs.BoolVar(&cfg.MeshProductionForwardingEnabled, "mesh-production-forwarding-enabled", getenvBool("RAP_MESH_PRODUCTION_FORWARDING_ENABLED", false), "Enable production forwarding gate; runtime still fail-closed if unavailable.")
fs.BoolVar(&cfg.MeshFabricSessionEnabled, "mesh-fabric-session-enabled", getenvBool("RAP_MESH_FABRIC_SESSION_ENABLED", false), "Enable authenticated fabric session endpoint.")
fs.BoolVar(&cfg.VPNFabricSessionTransportEnabled, "vpn-fabric-session-transport-enabled", getenvBool("RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED", false), "Route VPN packet transport over persistent fabric sessions.")
fs.BoolVar(&cfg.MeshQUICFabricEnabled, "mesh-quic-fabric-enabled", getenvBool("RAP_MESH_QUIC_FABRIC_ENABLED", false), "Enable QUIC/UDP fabric listener.")
fs.StringVar(&cfg.MeshQUICFabricListenAddr, "mesh-quic-fabric-listen-addr", getenv("RAP_MESH_QUIC_FABRIC_LISTEN_ADDR", ""), "QUIC/UDP fabric listen address.")
fs.IntVar(&cfg.VPNFabricSessionStreamShards, "vpn-fabric-session-stream-shards", getenvInt("RAP_VPN_FABRIC_SESSION_STREAM_SHARDS", 4), "VPN fabric-session stream shards per traffic class.")
fs.IntVar(&cfg.VPNFabricQUICMaxStreamsPerConn, "vpn-fabric-quic-max-streams-per-conn", getenvInt("RAP_VPN_FABRIC_QUIC_MAX_STREAMS_PER_CONN", 64), "Maximum logical fabric-session streams per cached VPN QUIC carrier connection.")
fs.IntVar(&cfg.VPNFabricQUICIdleTTLSeconds, "vpn-fabric-quic-idle-ttl-seconds", getenvInt("RAP_VPN_FABRIC_QUIC_IDLE_TTL_SECONDS", 300), "Idle TTL seconds for cached VPN QUIC carrier connections.")
fs.StringVar(&cfg.MeshListenAddr, "mesh-listen-addr", getenv("RAP_MESH_LISTEN_ADDR", ""), "Synthetic mesh HTTP listen address inside container.")
fs.StringVar(&cfg.MeshListenAddr, "mesh-listen-addr", getenv("RAP_MESH_LISTEN_ADDR", ""), "Historical synthetic mesh HTTP listen address inside container.")
fs.StringVar(&cfg.MeshListenPortMode, "mesh-listen-port-mode", getenv("RAP_MESH_LISTEN_PORT_MODE", ""), "Mesh listen port behavior: manual, auto, or disabled.")
fs.IntVar(&cfg.MeshListenAutoPortStart, "mesh-listen-auto-port-start", getenvInt("RAP_MESH_LISTEN_AUTO_PORT_START", 0), "First port used when mesh listen port mode is auto.")
fs.IntVar(&cfg.MeshListenAutoPortEnd, "mesh-listen-auto-port-end", getenvInt("RAP_MESH_LISTEN_AUTO_PORT_END", 0), "Last port used when mesh listen port mode is auto.")
@@ -941,13 +871,12 @@ func usage() {
rap-host-agent install -backend-url URL -cluster-id ID -join-token TOKEN -node-name NAME [docker options]
rap-host-agent install-windows -profile-url URL -install-token TOKEN [-node-name NAME] [windows options]
rap-host-agent install-linux -profile-url URL -install-token TOKEN [-node-name NAME] [linux/systemd options]
rap-host-agent install-updater -backend-url URL -cluster-id ID -state-dir DIR -container-name NAME
rap-host-agent update-host-agent -backend-url URL -cluster-id ID -state-dir DIR
rap-host-agent update-host-agent-loop -backend-url URL -cluster-id ID -state-dir DIR
rap-host-agent monitor-loop -backend-url URL -cluster-id ID -state-dir DIR --watch-container NAME
rap-host-agent monitor-once -backend-url URL -cluster-id ID -state-dir DIR --watch-container NAME
rap-host-agent fabric-session-smoke -mesh-url URL -token rap_fsn_TOKEN [-authority-payload VALUE -authority-signature VALUE]
rap-host-agent update -backend-url URL -cluster-id ID -node-id ID [-container-name NAME]
rap-host-agent update-loop -backend-url URL -cluster-id ID -node-id ID [-container-name NAME]
rap-host-agent install-updater (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -state-dir DIR -container-name NAME
rap-host-agent update-host-agent (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -state-dir DIR
rap-host-agent update-host-agent-loop (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -state-dir DIR
rap-host-agent monitor-loop (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -state-dir DIR --watch-container NAME
rap-host-agent monitor-once (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -state-dir DIR --watch-container NAME
rap-host-agent update (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -node-id ID [-container-name NAME]
rap-host-agent update-loop (-backend-url URL | -fabric-registry-records-json JSON) -cluster-id ID -node-id ID [-container-name NAME]
rap-host-agent status [-container-name NAME]`)
}
File diff suppressed because it is too large Load Diff
@@ -21,6 +21,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"
@@ -204,7 +205,7 @@ func TestRouteManagerDecisionsFromControlPlaneConsumesRebuildRouteCommand(t *tes
}
decision := decisions[0]
if decision.RouteID != "route-primary" ||
decision.RebuildStatus != "pending_degraded_fallback" ||
decision.RebuildStatus != "pending_degraded_route_state" ||
decision.DecisionSource != "service_channel_remediation_command" ||
decision.RebuildRequestID != "cmd-rebuild" {
t.Fatalf("unexpected rebuild remediation decision: %+v", decision)
@@ -279,7 +280,6 @@ func TestGatewayTransportForAssignmentUsesFabricSessionWhenEnabled(t *testing.T)
&syntheticMeshState{
ProductionForwardTransport: noopProductionForwardTransport{},
VPNFabricInbox: inbox,
VPNFabricSessionPeers: mesh.NewFabricSessionPeerManager(),
PeerEndpointCandidates: map[string][]mesh.PeerEndpointCandidate{
"entry-1": {{
EndpointID: "entry-1-quic",
@@ -322,7 +322,6 @@ func TestGatewayTransportForAssignmentFallsBackWhenFabricSessionUnavailable(t *t
&syntheticMeshState{
ProductionForwardTransport: noopProductionForwardTransport{},
VPNFabricInbox: inbox,
VPNFabricSessionPeers: mesh.NewFabricSessionPeerManager(),
PeerEndpoints: map[string]string{},
Routes: []mesh.SyntheticRoute{{
RouteID: "route-exit-entry",
@@ -424,6 +423,496 @@ func testMainQUICCertSHA256(t *testing.T, config *tls.Config) string {
return hex.EncodeToString(sum[:])
}
func TestFabricControlForwardHandlerUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
var req client.RawControlRequest
if err := json.Unmarshal(payload, &req); err != nil {
return nil, err
}
if req.Path != "/auth/login" {
return nil, fmt.Errorf("unexpected path %s", req.Path)
}
return json.Marshal(client.RawControlResponse{StatusCode: 200, Body: json.RawMessage(`{"via":"fabric"}`)})
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
now := time.Now().UTC()
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate key: %v", err)
}
issuer := mesh.FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: mesh.FabricRegistryAuthorityControl, PublicKey: publicKey}
record := mesh.FabricRegistryGossipRecord{
SchemaVersion: mesh.FabricRegistryGossipRecordSchema,
ClusterID: "cluster-1",
Service: mesh.FabricRegistryServiceControlAPI,
Scope: mesh.FabricRegistryScopeCluster,
Epoch: 1,
IssuedAt: now.Add(-time.Minute),
ExpiresAt: now.Add(time.Hour),
IssuerNodeID: "authority-1",
IssuerRole: mesh.FabricRegistryAuthorityControl,
Endpoints: []mesh.FabricRegistryEndpoint{{
EndpointID: "control-a",
Address: "quic://" + server.Addr().String(),
Transport: "direct_quic",
PeerCertSHA256: testMainQUICCertSHA256(t, tlsConfig),
}},
}
signed, err := mesh.SignFabricRegistryGossipRecord(record, issuer, privateKey)
if err != nil {
t.Fatalf("sign registry record: %v", err)
}
registry := mesh.NewFabricRegistry()
if _, _, err := registry.ApplyGossipRecord(signed, mesh.FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []mesh.FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}, true); err != nil {
t.Fatalf("apply registry record: %v", err)
}
transport := mesh.NewQUICFabricTransport(nil)
transport.SetLocalPeerID("node-a")
handler := fabricControlForwardHandlerFromMeshState(nil, state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}, &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: transport,
ListenerRuntimeConfig: config.Config{MeshRegion: "test"},
})
payload, err := handler(context.Background(), []byte(`{"method":"POST","path":"/auth/login","body":{"user":"a"}}`))
if err != nil {
t.Fatalf("fabric control handler: %v", err)
}
var response client.RawControlResponse
if err := json.Unmarshal(payload, &response); err != nil {
t.Fatalf("decode raw control response: %v", err)
}
if response.StatusCode != 200 || string(response.Body) != `{"via":"fabric"}` {
t.Fatalf("response = %+v", response)
}
}
func TestHeartbeatViaFabricControlUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
var req client.RawControlRequest
if err := json.Unmarshal(payload, &req); err != nil {
return nil, err
}
if req.Method != http.MethodPost || req.Path != "/clusters/cluster-1/nodes/node-a/heartbeats" {
return nil, fmt.Errorf("unexpected request: %+v", req)
}
return json.Marshal(client.RawControlResponse{
StatusCode: 202,
Body: json.RawMessage(`{
"heartbeat":{"id":"hb-1"},
"testing_flags":{"enabled":true,"synthetic_links_enabled":true,"applied_scopes":["cluster"]},
"update_hint":{"schema_version":"rap.node_update_hint.v1","check_now":true,"generation":"gen-1"}
}`),
})
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
now := time.Now().UTC()
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate key: %v", err)
}
issuer := mesh.FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: mesh.FabricRegistryAuthorityControl, PublicKey: publicKey}
record := mesh.FabricRegistryGossipRecord{
SchemaVersion: mesh.FabricRegistryGossipRecordSchema,
ClusterID: "cluster-1",
Service: mesh.FabricRegistryServiceControlAPI,
Scope: mesh.FabricRegistryScopeCluster,
Epoch: 1,
IssuedAt: now.Add(-time.Minute),
ExpiresAt: now.Add(time.Hour),
IssuerNodeID: "authority-1",
IssuerRole: mesh.FabricRegistryAuthorityControl,
Endpoints: []mesh.FabricRegistryEndpoint{{
EndpointID: "control-a",
Address: "quic://" + server.Addr().String(),
Transport: "direct_quic",
PeerCertSHA256: testMainQUICCertSHA256(t, tlsConfig),
}},
}
signed, err := mesh.SignFabricRegistryGossipRecord(record, issuer, privateKey)
if err != nil {
t.Fatalf("sign registry record: %v", err)
}
registry := mesh.NewFabricRegistry()
if _, _, err := registry.ApplyGossipRecord(signed, mesh.FabricRegistryVerificationPolicy{
LocalClusterID: "cluster-1",
TrustedIssuers: []mesh.FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}, true); err != nil {
t.Fatalf("apply registry record: %v", err)
}
response, viaFabric, err := heartbeatViaFabricControl(context.Background(), state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}, &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
}, client.HeartbeatRequest{HealthStatus: "healthy"})
if err != nil {
t.Fatalf("heartbeat via fabric: %v", err)
}
if !viaFabric || !response.TestingFlags.Enabled || response.UpdateHint == nil || response.UpdateHint.Generation != "gen-1" {
t.Fatalf("unexpected heartbeat response viaFabric=%t response=%+v", viaFabric, response)
}
}
func TestSyntheticMeshConfigRefreshUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
var req client.RawControlRequest
if err := json.Unmarshal(payload, &req); err != nil {
return nil, err
}
if req.Method != http.MethodGet || req.Path != "/clusters/cluster-1/nodes/node-a/mesh/synthetic-config" {
return nil, fmt.Errorf("unexpected request: %+v", req)
}
return json.Marshal(client.RawControlResponse{
StatusCode: 200,
Body: json.RawMessage(`{
"synthetic_mesh_config":{
"enabled":true,
"config_version":"fabric-gen-1",
"peer_directory_version":"pd-1",
"policy_version":"pol-1",
"peer_endpoints":{},
"routes":[]
}
}`),
})
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
registry := signedTestControlRegistry(t, "cluster-1", "quic://"+server.Addr().String(), testMainQUICCertSHA256(t, tlsConfig))
loaded, err := loadSyntheticMeshConfigRuntime(context.Background(), config.Config{}, state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}, nil, &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
})
if err != nil {
t.Fatalf("load synthetic mesh config via fabric: %v", err)
}
if loaded.Source != "control_plane" || loaded.ConfigVersion != "fabric-gen-1" {
t.Fatalf("loaded = %+v", loaded)
}
}
func TestReportMeshLinkUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
var received client.RawControlRequest
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
if err := json.Unmarshal(payload, &received); err != nil {
return nil, err
}
if received.Method != http.MethodPost || received.Path != "/clusters/cluster-1/mesh/links" {
return nil, fmt.Errorf("unexpected request: %+v", received)
}
return json.Marshal(client.RawControlResponse{StatusCode: 202, Body: json.RawMessage(`{"ok":true}`)})
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
registry := signedTestControlRegistry(t, "cluster-1", "quic://"+server.Addr().String(), testMainQUICCertSHA256(t, tlsConfig))
err = reportMeshLink(context.Background(), nil, state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}, &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
}, client.MeshLinkObservationRequest{
SourceNodeID: "node-a",
TargetNodeID: "node-b",
LinkStatus: "reachable",
})
if err != nil {
t.Fatalf("report mesh link via fabric: %v", err)
}
if len(received.Body) == 0 || !strings.Contains(string(received.Body), `"target_node_id":"node-b"`) {
t.Fatalf("unexpected received body: %s", string(received.Body))
}
}
func TestReportTelemetryUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
var received client.RawControlRequest
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
if err := json.Unmarshal(payload, &received); err != nil {
return nil, err
}
if received.Method != http.MethodPost || received.Path != "/clusters/cluster-1/nodes/node-a/telemetry" {
return nil, fmt.Errorf("unexpected request: %+v", received)
}
return json.Marshal(client.RawControlResponse{StatusCode: 202, Body: json.RawMessage(`{"ok":true}`)})
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
registry := signedTestControlRegistry(t, "cluster-1", "quic://"+server.Addr().String(), testMainQUICCertSHA256(t, tlsConfig))
err = reportTelemetry(context.Background(), nil, state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}, &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
}, client.TelemetryRequest{Payload: map[string]any{"fabric": "quic"}})
if err != nil {
t.Fatalf("report telemetry via fabric: %v", err)
}
if len(received.Body) == 0 || !strings.Contains(string(received.Body), `"fabric":"quic"`) {
t.Fatalf("unexpected received body: %s", string(received.Body))
}
}
func TestWorkloadControlUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
var paths []string
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
var req client.RawControlRequest
if err := json.Unmarshal(payload, &req); err != nil {
return nil, err
}
paths = append(paths, req.Method+" "+req.Path)
switch req.Path {
case "/clusters/cluster-1/nodes/node-a/workloads/desired":
return json.Marshal(client.RawControlResponse{
StatusCode: 200,
Body: json.RawMessage(`{"desired_workloads":[{"service_type":"vpn-egress","desired_state":"enabled","runtime_mode":"node"}]}`),
})
case "/clusters/cluster-1/nodes/node-a/workloads/vpn-egress/status":
if len(req.Body) == 0 || !strings.Contains(string(req.Body), `"reported_state":"running"`) {
return nil, fmt.Errorf("unexpected status body: %s", string(req.Body))
}
return json.Marshal(client.RawControlResponse{StatusCode: 204, Body: json.RawMessage(`{}`)})
default:
return nil, fmt.Errorf("unexpected request: %+v", req)
}
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
registry := signedTestControlRegistry(t, "cluster-1", "quic://"+server.Addr().String(), testMainQUICCertSHA256(t, tlsConfig))
meshState := &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
}
identity := state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}
desired, err := desiredWorkloads(context.Background(), nil, identity, meshState)
if err != nil {
t.Fatalf("desired workloads via fabric: %v", err)
}
if len(desired) != 1 || desired[0].ServiceType != "vpn-egress" {
t.Fatalf("desired = %+v", desired)
}
if err := reportSingleWorkloadStatus(context.Background(), nil, identity, meshState, "vpn-egress", client.WorkloadStatusRequest{ReportedState: "running"}); err != nil {
t.Fatalf("report workload status via fabric: %v", err)
}
want := []string{
"GET /clusters/cluster-1/nodes/node-a/workloads/desired",
"POST /clusters/cluster-1/nodes/node-a/workloads/vpn-egress/status",
}
if !reflect.DeepEqual(paths, want) {
t.Fatalf("paths = %+v, want %+v", paths, want)
}
}
func TestAdminRuntimeProjectionUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
var received client.RawControlRequest
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
if err := json.Unmarshal(payload, &received); err != nil {
return nil, err
}
if received.Method != http.MethodPost || received.Path != "/clusters/cluster-1/nodes/node-a/admin-runtime/projection" {
return nil, fmt.Errorf("unexpected request: %+v", received)
}
return json.Marshal(client.RawControlResponse{
StatusCode: 200,
Body: json.RawMessage(`{"schema_version":"rap.admin_runtime_projection.v1","status":"ok","status_code":200,"body":{"page":"cluster"}}`),
})
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
registry := signedTestControlRegistry(t, "cluster-1", "quic://"+server.Addr().String(), testMainQUICCertSHA256(t, tlsConfig))
projection, err := controlAPIProjectionClient{
Identity: state.Identity{ClusterID: "cluster-1", NodeID: "node-a"},
MeshState: &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
},
}.Project(context.Background(), webingress.ControlAPIProjectionRequest{
SchemaVersion: "rap.web_ingress_projection.v1",
Method: http.MethodGet,
Path: "/cluster-admin",
Scope: "cluster",
ServiceClass: "cluster_admin",
})
if err != nil {
t.Fatalf("admin projection via fabric: %v", err)
}
if projection.StatusCode != 200 || string(projection.Body) != `{"page":"cluster"}` {
t.Fatalf("projection = %+v", projection)
}
if len(received.Body) == 0 || !strings.Contains(string(received.Body), `"service_class":"cluster_admin"`) {
t.Fatalf("unexpected received body: %s", string(received.Body))
}
}
func TestVPNAssignmentControlUsesRegistryQUICControlAPI(t *testing.T) {
tlsConfig := testMainQUICTLSConfig(t)
var paths []string
server, err := mesh.StartQUICFabricServer(context.Background(), mesh.QUICFabricServerConfig{
ListenAddr: "127.0.0.1:0",
TLSConfig: tlsConfig,
FabricControlHandler: func(_ context.Context, payload []byte) ([]byte, error) {
var req client.RawControlRequest
if err := json.Unmarshal(payload, &req); err != nil {
return nil, err
}
paths = append(paths, req.Method+" "+req.Path)
switch req.Path {
case "/clusters/cluster-1/nodes/node-a/vpn/assignments":
return json.Marshal(client.RawControlResponse{
StatusCode: 200,
Body: json.RawMessage(`{"vpn_assignments":[{"vpn_connection_id":"vpn-1","desired_state":"enabled","assignment_reason":"eligible_candidate"}]}`),
})
case "/clusters/cluster-1/nodes/node-a/vpn/assignments/vpn-1/lease/acquire":
return json.Marshal(client.RawControlResponse{
StatusCode: 201,
Body: json.RawMessage(`{"lease":{"lease_id":"lease-1","owner_node_id":"node-a","lease_generation":1,"status":"active"}}`),
})
case "/clusters/cluster-1/nodes/node-a/vpn/assignments/vpn-1/lease/lease-1/renew":
return json.Marshal(client.RawControlResponse{StatusCode: 204, Body: json.RawMessage(`{}`)})
case "/clusters/cluster-1/nodes/node-a/vpn/assignments/vpn-1/status":
if len(req.Body) == 0 || !strings.Contains(string(req.Body), `"observed_status":"assigned"`) {
return nil, fmt.Errorf("unexpected status body: %s", string(req.Body))
}
return json.Marshal(client.RawControlResponse{StatusCode: 204, Body: json.RawMessage(`{}`)})
default:
return nil, fmt.Errorf("unexpected request: %+v", req)
}
},
})
if err != nil {
t.Fatalf("start quic fabric server: %v", err)
}
defer server.Close()
registry := signedTestControlRegistry(t, "cluster-1", "quic://"+server.Addr().String(), testMainQUICCertSHA256(t, tlsConfig))
meshState := &syntheticMeshState{
FabricRegistry: registry,
VPNFabricQUICTransport: mesh.NewQUICFabricTransport(nil),
}
identity := state.Identity{ClusterID: "cluster-1", NodeID: "node-a"}
assignments, err := nodeVPNAssignments(context.Background(), nil, identity, meshState)
if err != nil {
t.Fatalf("vpn assignments via fabric: %v", err)
}
if len(assignments) != 1 || assignments[0].VPNConnectionID != "vpn-1" {
t.Fatalf("assignments = %+v", assignments)
}
lease, err := acquireNodeVPNAssignmentLease(context.Background(), nil, identity, meshState, "vpn-1", client.NodeVPNAssignmentLeaseAcquireRequest{TTLSeconds: 300})
if err != nil {
t.Fatalf("acquire lease via fabric: %v", err)
}
if lease == nil || lease.LeaseID != "lease-1" {
t.Fatalf("lease = %+v", lease)
}
if err := renewNodeVPNAssignmentLease(context.Background(), nil, identity, meshState, "vpn-1", "lease-1", client.NodeVPNAssignmentLeaseRenewRequest{TTLSeconds: 300}); err != nil {
t.Fatalf("renew lease via fabric: %v", err)
}
if err := reportNodeVPNAssignmentStatus(context.Background(), nil, identity, meshState, "vpn-1", client.NodeVPNAssignmentStatusRequest{ObservedStatus: "assigned"}); err != nil {
t.Fatalf("report status via fabric: %v", err)
}
want := []string{
"GET /clusters/cluster-1/nodes/node-a/vpn/assignments",
"POST /clusters/cluster-1/nodes/node-a/vpn/assignments/vpn-1/lease/acquire",
"POST /clusters/cluster-1/nodes/node-a/vpn/assignments/vpn-1/lease/lease-1/renew",
"POST /clusters/cluster-1/nodes/node-a/vpn/assignments/vpn-1/status",
}
if !reflect.DeepEqual(paths, want) {
t.Fatalf("paths = %+v, want %+v", paths, want)
}
}
func signedTestControlRegistry(t *testing.T, clusterID string, endpoint string, certSHA256 string) *mesh.FabricRegistry {
t.Helper()
now := time.Now().UTC()
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate key: %v", err)
}
issuer := mesh.FabricRegistryTrustedIssuer{IssuerID: "authority-1", Role: mesh.FabricRegistryAuthorityControl, PublicKey: publicKey}
record := mesh.FabricRegistryGossipRecord{
SchemaVersion: mesh.FabricRegistryGossipRecordSchema,
ClusterID: clusterID,
Service: mesh.FabricRegistryServiceControlAPI,
Scope: mesh.FabricRegistryScopeCluster,
Epoch: 1,
IssuedAt: now.Add(-time.Minute),
ExpiresAt: now.Add(time.Hour),
IssuerNodeID: "authority-1",
IssuerRole: mesh.FabricRegistryAuthorityControl,
Endpoints: []mesh.FabricRegistryEndpoint{{
EndpointID: "control-a",
Address: endpoint,
Transport: "direct_quic",
PeerCertSHA256: certSHA256,
}},
}
signed, err := mesh.SignFabricRegistryGossipRecord(record, issuer, privateKey)
if err != nil {
t.Fatalf("sign registry record: %v", err)
}
registry := mesh.NewFabricRegistry()
if _, _, err := registry.ApplyGossipRecord(signed, mesh.FabricRegistryVerificationPolicy{
LocalClusterID: clusterID,
TrustedIssuers: []mesh.FabricRegistryTrustedIssuer{issuer},
RequiredSignatures: 1,
Now: now,
}, true); err != nil {
t.Fatalf("apply registry record: %v", err)
}
return registry
}
func TestRouteManagerDecisionsFromControlPlaneKeepsExplicitRemediationCommand(t *testing.T) {
now := time.Now().UTC()
report := &client.RoutePathDecisionReport{Decisions: []client.RoutePathDecision{{
@@ -493,9 +982,10 @@ func TestFabricServiceChannelAccessStatsReportsDataPlaneViolations(t *testing.T)
OccurredAt: time.Unix(10, 0).UTC(),
})
report := stats.Report(time.Unix(20, 0).UTC())
if report["backend_fallback_blocked"] != int64(1) ||
if report["degraded_compatibility_blocked"] != int64(1) ||
report["fabric_route_send_failure"] != int64(1) ||
report["last_data_plane_violation_status"] != "fabric_route_send_failed_backend_fallback_blocked" ||
report["last_data_plane_violation_status"] != "degraded_compatibility_blocked" ||
report["last_data_plane_violation_status_raw"] != "fabric_route_send_failed_backend_fallback_blocked" ||
report["last_data_plane_violation_reason"] != "mesh synthetic route not found" {
t.Fatalf("unexpected violation report: %+v", report)
}
@@ -790,7 +1280,56 @@ func TestVerifyEnrollmentBootstrapRejectsPinnedAuthorityMismatch(t *testing.T) {
}
}
func TestNormalizeLoadedSyntheticMeshConfigMigratesLegacyControlPlaneSurfaces(t *testing.T) {
func TestLoadFabricRegistryBootstrapAcceptsSignedCandidate(t *testing.T) {
now := time.Now().UTC()
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
record := mesh.FabricRegistryGossipRecord{
SchemaVersion: mesh.FabricRegistryGossipRecordSchema,
ClusterID: "cluster-1",
Service: mesh.FabricRegistryServiceControlAPI,
Scope: mesh.FabricRegistryScopeCluster,
Epoch: 1,
IssuedAt: now.Add(-time.Minute),
ExpiresAt: now.Add(time.Hour),
IssuerNodeID: "authority-node",
IssuerRole: mesh.FabricRegistryAuthorityControl,
Endpoints: []mesh.FabricRegistryEndpoint{
{EndpointID: "control-a", Address: "quic://control.example.test:19443", Transport: "direct_quic"},
},
}
signed, err := mesh.SignFabricRegistryGossipRecord(record, mesh.FabricRegistryTrustedIssuer{
IssuerID: "cluster-authority",
Role: mesh.FabricRegistryAuthorityControl,
}, privateKey)
if err != nil {
t.Fatalf("sign registry record: %v", err)
}
raw, err := json.Marshal([]mesh.FabricRegistryGossipRecord{signed})
if err != nil {
t.Fatalf("marshal registry records: %v", err)
}
registry, report := loadFabricRegistryBootstrap(config.Config{
ClusterAuthorityPublicKey: base64.StdEncoding.EncodeToString(publicKey),
FabricRegistryRecordsJSON: string(raw),
}, state.Identity{ClusterID: "cluster-1"})
if registry == nil || report.Total != 1 || report.Candidate != 1 || report.Rejected != 0 {
t.Fatalf("unexpected registry bootstrap report: %+v registry=%v", report, registry)
}
if _, ok := registry.Active("cluster-1", mesh.FabricRegistryServiceControlAPI, mesh.FabricRegistryScopeCluster, "", now); ok {
t.Fatal("bootstrap record should remain candidate until live verification")
}
if !registry.MarkLiveVerified("cluster-1", mesh.FabricRegistryServiceControlAPI, mesh.FabricRegistryScopeCluster, "", now) {
t.Fatal("MarkLiveVerified = false")
}
if _, ok := registry.Active("cluster-1", mesh.FabricRegistryServiceControlAPI, mesh.FabricRegistryScopeCluster, "", now); !ok {
t.Fatal("expected active record after live verification")
}
}
func TestNormalizeLoadedSyntheticMeshConfigMigratesNonQUICControlPlaneSurfaces(t *testing.T) {
loaded := loadedSyntheticMeshConfig{
PeerEndpoints: map[string]string{
"node-a": "https://node-a.example.test:443",
@@ -798,7 +1337,7 @@ func TestNormalizeLoadedSyntheticMeshConfigMigratesLegacyControlPlaneSurfaces(t
PeerEndpointCandidates: map[string][]mesh.PeerEndpointCandidate{
"node-b": {
{
EndpointID: "node-b-legacy",
EndpointID: "node-b-http-migration",
NodeID: "node-b",
Transport: "direct_http",
Address: "https://node-b.example.test:443",
@@ -816,7 +1355,7 @@ func TestNormalizeLoadedSyntheticMeshConfigMigratesLegacyControlPlaneSurfaces(t
},
RendezvousLeases: []mesh.PeerRendezvousLease{
{
LeaseID: "lease-legacy",
LeaseID: "lease-http-migration",
PeerNodeID: "node-b",
RelayNodeID: "node-r",
RelayEndpoint: "http://node-r.example.test:19001",
@@ -824,7 +1363,7 @@ func TestNormalizeLoadedSyntheticMeshConfigMigratesLegacyControlPlaneSurfaces(t
},
},
RoutePathDecisions: &client.RoutePathDecisionReport{
Decisions: []client.RoutePathDecision{{DecisionID: "decision-legacy", SelectedRelayEndpoint: "http://node-r.example.test:19001"}},
Decisions: []client.RoutePathDecision{{DecisionID: "decision-http-migration", SelectedRelayEndpoint: "http://node-r.example.test:19001"}},
},
}
normalizeLoadedSyntheticMeshConfigQUICOnly(&loaded)
@@ -849,14 +1388,14 @@ func TestNormalizeLoadedSyntheticMeshConfigMigratesLegacyControlPlaneSurfaces(t
}
}
func TestValidateLoadedSyntheticMeshConfigRejectsUnnormalizedLegacyControlPlaneSurfaces(t *testing.T) {
func TestValidateLoadedSyntheticMeshConfigRejectsUnnormalizedNonQUICControlPlaneSurfaces(t *testing.T) {
err := validateLoadedSyntheticMeshConfigQUICOnly(loadedSyntheticMeshConfig{
RoutePathDecisions: &client.RoutePathDecisionReport{
Decisions: []client.RoutePathDecision{{DecisionID: "decision-legacy", SelectedRelayEndpoint: "http://node-r.example.test:19001"}},
Decisions: []client.RoutePathDecision{{DecisionID: "decision-http-migration", SelectedRelayEndpoint: "http://node-r.example.test:19001"}},
},
})
if err == nil || !strings.Contains(err.Error(), "QUIC selected relay endpoint") {
t.Fatalf("expected legacy selected relay endpoint rejection, got %v", err)
t.Fatalf("expected non-QUIC selected relay endpoint rejection, got %v", err)
}
}
@@ -942,7 +1481,6 @@ func TestHeartbeatPayloadIncludesMeshEndpointReport(t *testing.T) {
MeshRegion: "eu",
MeshSyntheticRuntimeEnabled: true,
MeshProductionForwardingEnabled: true,
MeshFabricSessionEnabled: true,
VPNFabricSessionTransportEnabled: true,
VPNFabricSessionStreamShards: 6,
VPNFabricQUICMaxStreamsPerConn: 24,
@@ -952,7 +1490,6 @@ func TestHeartbeatPayloadIncludesMeshEndpointReport(t *testing.T) {
ClusterID: "cluster-1",
NodeID: "node-a",
}, &syntheticMeshState{
VPNFabricSessionPeers: mesh.NewFabricSessionPeerManager(),
VPNFabricQUICTransport: func() *mesh.QUICFabricTransport {
transport := mesh.NewQUICFabricTransport(nil)
transport.MaxStreamsPerConn = 24
@@ -1010,8 +1547,7 @@ func TestHeartbeatPayloadIncludesMeshEndpointReport(t *testing.T) {
if report, ok := payload.Metadata["vpn_fabric_session_transport_report"].(map[string]any); !ok ||
report["packet_payload"] != "rap.vpn_packet_batch.fabric.v1" ||
report["transport"] != "fabric_session_binary_frames" ||
report["stream_shards_per_class"] != 6 ||
report["peer_sessions"] == nil {
report["stream_shards_per_class"] != 6 {
t.Fatalf("vpn fabric session report missing: %+v", payload.Metadata)
} else if report["quic_sessions"] == nil || report["quic_max_streams_per_conn"] != 24 {
t.Fatalf("vpn fabric quic session report missing: %+v", report)
@@ -1242,14 +1778,14 @@ func TestVPNFabricSessionTargetPrefersRankedQUICCandidate(t *testing.T) {
}
}
func TestVPNFabricSessionTargetFallsBackToLegacyPeerEndpoint(t *testing.T) {
func TestVPNFabricSessionTargetRejectsNonQUICPeerEndpoint(t *testing.T) {
_, ok := vpnFabricSessionTarget(&syntheticMeshState{
PeerEndpoints: map[string]string{
"node-b": "https://node-b.example.test:443/",
},
}, "node-b")
if ok {
t.Fatal("legacy peer endpoint unexpectedly produced a QUIC target")
t.Fatal("non-QUIC peer endpoint unexpectedly produced a QUIC target")
}
}
@@ -1257,7 +1793,7 @@ func TestVPNFabricSessionTargetsIncludeRankedQUICCandidatesWithoutLegacyFallback
now := time.Now().UTC()
targets := vpnFabricSessionTargets(&syntheticMeshState{
PeerEndpoints: map[string]string{
"node-b": "https://node-b-legacy.example.test:443/",
"node-b": "https://node-b-http-migration.example.test:443/",
},
PeerEndpointCandidates: map[string][]mesh.PeerEndpointCandidate{
"node-b": {
@@ -2731,7 +3267,7 @@ func TestWebIngressForwardHandlerFromConfigVerifiesSignedEnvelope(t *testing.T)
keyID := "web-key-1"
handler := webIngressForwardHandlerFromConfig(config.Config{
WebIngressTrustedKeysJSON: webingress.TrustedKeysJSONForPublicKey(keyID, publicKey),
}, state.Identity{ClusterID: "cluster-1", NodeID: "node-1"}, nil)
}, state.Identity{ClusterID: "cluster-1", NodeID: "node-1"}, nil, nil)
if handler == nil {
t.Fatal("handler is nil")
}
@@ -2780,10 +3316,10 @@ func TestWebIngressForwardHandlerFromConfigVerifiesSignedEnvelope(t *testing.T)
}
func TestWebIngressForwardHandlerFromConfigDisabledWithoutTrustedKeys(t *testing.T) {
if handler := webIngressForwardHandlerFromConfig(config.Config{}, state.Identity{}, nil); handler != nil {
if handler := webIngressForwardHandlerFromConfig(config.Config{}, state.Identity{}, nil, nil); handler != nil {
t.Fatal("handler should be nil without trusted keys")
}
if handler := webIngressForwardHandlerFromConfig(config.Config{WebIngressTrustedKeysJSON: `{"bad":"key"}`}, state.Identity{}, nil); handler != nil {
if handler := webIngressForwardHandlerFromConfig(config.Config{WebIngressTrustedKeysJSON: `{"bad":"key"}`}, state.Identity{}, nil, nil); handler != nil {
t.Fatal("handler should be nil with invalid trusted keys")
}
}