Record project continuation changes

This commit is contained in:
2026-05-12 21:02:29 +03:00
parent 3059d1d7a3
commit 8f69d53193
339 changed files with 101111 additions and 1769 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,216 @@
package cluster
import (
"context"
"testing"
"time"
)
func TestVPNPacketHubPopBatchAndStatsKeys(t *testing.T) {
hub := newVPNPacketHub()
key := vpnPacketKey{
ClusterID: "cluster-1",
VPNConnectionID: "vpn-1",
Direction: vpnDirectionClientToGateway,
}
packetA := []byte{
0x45, 0x00, 0x00, 20,
0x00, 0x01, 0x00, 0x00,
64, 17, 0, 0,
192, 168, 0, 1,
192, 168, 0, 2,
0x00, 0x50, 0x01, 0xBB,
}
packetB := make([]byte, len(packetA))
copy(packetB, packetA)
packetB[19] = 0xBA
hub.Push(key, packetA)
hub.Push(key, packetB)
packets := hub.PopBatch(context.Background(), key, 0, vpnPacketBatchMaxPackets, vpnPacketBatchMaxBytes)
if len(packets) != 2 {
t.Fatalf("expected 2 packets in batch, got %d", len(packets))
}
statsAny := hub.Snapshot("cluster-1", "vpn-1")[vpnDirectionClientToGateway]
stats, ok := statsAny.(map[string]any)
if !ok {
t.Fatalf("unexpected stats payload type: %T", statsAny)
}
for _, keyName := range []string{
"pushed",
"pushed_bytes",
"popped",
"popped_bytes",
"window_push_rate_pps",
"window_pop_rate_pps",
"window_push_rate_mbps",
"window_pop_rate_mbps",
"window_push_packets",
"window_pop_packets",
"queue_depth",
"queue_depths",
"queue_depth_max",
"queue_depth_high_watermark",
"queue_depth_high_at",
"shard_depth_high_watermark",
"shard_depth_high_at",
"queue_capacity",
"queue_shard_capacity",
"queue_full_drops",
"requeue_drops",
"cleared_stale_packets",
"flow_shard_count",
"flow_isolation",
} {
if _, found := stats[keyName]; !found {
t.Fatalf("missing vpn packet stat key %s", keyName)
}
}
if got, ok := stats["popped"].(uint64); !ok || got != 2 {
t.Fatalf("expected popped=2, got %v (ok=%v)", stats["popped"], ok)
}
if got, ok := stats["pushed"].(uint64); !ok || got != 2 {
t.Fatalf("expected pushed=2, got %v (ok=%v)", stats["pushed"], ok)
}
if got, ok := stats["queue_depth_high_watermark"].(int); !ok || got < 1 {
t.Fatalf("expected queue depth high watermark, got %v (ok=%v)", stats["queue_depth_high_watermark"], ok)
}
}
func TestVPNPacketHubGatherBehavior(t *testing.T) {
hub := newVPNPacketHub()
key := vpnPacketKey{
ClusterID: "cluster-1",
VPNConnectionID: "vpn-1",
Direction: vpnDirectionGatewayToClient,
}
packet := []byte{
0x45, 0x00, 0x00, 20,
0x00, 0x01, 0x00, 0x00,
64, 6, 0, 0,
10, 0, 0, 1,
10, 0, 0, 2,
0x12, 0x34, 0x56, 0x78,
}
hub.Push(key, packet)
hub.Push(key, packet)
hub.Push(key, packet)
first, ok := hub.Pop(context.Background(), key, 0)
if !ok {
t.Fatal("expected packet from queue")
}
batch := hub.PopBatch(context.Background(), key, 0, 1, 1024)
if len(batch) != 1 {
t.Fatalf("expected 1 packet because batch limit 1, got %d", len(batch))
}
_ = first
}
func TestVPNPacketHubFlowShardsReportDepths(t *testing.T) {
hub := newVPNPacketHub()
key := vpnPacketKey{
ClusterID: "cluster-1",
VPNConnectionID: "vpn-1",
Direction: vpnDirectionGatewayToClient,
}
for i := byte(1); i <= 8; i++ {
packet := []byte{
0x45, 0x00, 0x00, 24,
0x00, i, 0x00, 0x00,
64, 6, 0, 0,
10, 0, 0, i,
192, 168, 200, i,
0x12, i, 0x56, i,
}
if err := hub.Push(key, packet); err != nil {
t.Fatalf("push packet %d: %v", i, err)
}
}
statsAny := hub.Snapshot("cluster-1", "vpn-1")[vpnDirectionGatewayToClient]
stats, ok := statsAny.(map[string]any)
if !ok {
t.Fatalf("unexpected stats payload type: %T", statsAny)
}
if got, ok := stats["queue_depth"].(int); !ok || got != 8 {
t.Fatalf("expected queue_depth=8, got %v (ok=%v)", stats["queue_depth"], ok)
}
depths, ok := stats["queue_depths"].([]int)
if !ok {
t.Fatalf("unexpected queue_depths payload type: %T", stats["queue_depths"])
}
if len(depths) != vpnPacketFlowShardCount {
t.Fatalf("expected %d queue shards, got %d", vpnPacketFlowShardCount, len(depths))
}
nonEmpty := 0
for _, depth := range depths {
if depth > 0 {
nonEmpty++
}
}
if nonEmpty < 2 {
t.Fatalf("expected packets to be distributed across at least 2 shards, got depths=%v", depths)
}
}
func TestVPNPacketHubClearDoesNotCountAsDrop(t *testing.T) {
hub := newVPNPacketHub()
key := vpnPacketKey{
ClusterID: "cluster-1",
VPNConnectionID: "vpn-1",
Direction: vpnDirectionClientToGateway,
}
packet := []byte{
0x45, 0x00, 0x00, 20,
0x00, 0x01, 0x00, 0x00,
64, 6, 0, 0,
10, 0, 0, 1,
10, 0, 0, 2,
0x12, 0x34, 0x56, 0x78,
}
if err := hub.Push(key, packet); err != nil {
t.Fatalf("push packet: %v", err)
}
if cleared := hub.Clear(key); cleared != 1 {
t.Fatalf("expected cleared=1, got %d", cleared)
}
statsAny := hub.Snapshot("cluster-1", "vpn-1")[vpnDirectionClientToGateway]
stats, ok := statsAny.(map[string]any)
if !ok {
t.Fatalf("unexpected stats payload type: %T", statsAny)
}
if got, ok := stats["dropped"].(uint64); !ok || got != 0 {
t.Fatalf("expected dropped=0 for stale clear, got %v (ok=%v)", stats["dropped"], ok)
}
if got, ok := stats["cleared_stale_packets"].(uint64); !ok || got != 1 {
t.Fatalf("expected cleared_stale_packets=1, got %v (ok=%v)", stats["cleared_stale_packets"], ok)
}
}
func TestVPNClientDiagnosticStopCommandDrainsPendingWork(t *testing.T) {
hub := newVPNClientDiagnosticHub()
hub.Enqueue("cluster-1", "device-1", map[string]any{"type": "vpn_page_probe", "url": "https://speedtest.rt.ru/"})
hub.Enqueue("cluster-1", "device-1", map[string]any{"type": "vpn_tcp_connect", "host": "192.168.200.95"})
hub.Enqueue("cluster-1", "device-1", map[string]any{"type": "stop_vpn"})
item, ok := hub.Pop(context.Background(), "cluster-1", "device-1", time.Millisecond)
if !ok {
t.Fatal("expected priority stop command")
}
if got, _ := item.Payload["type"].(string); got != "stop_vpn" {
t.Fatalf("first command = %q, want stop_vpn", got)
}
if item, ok := hub.Pop(context.Background(), "cluster-1", "device-1", 0); ok {
t.Fatalf("expected old commands to be drained, got %#v", item.Payload)
}
}
File diff suppressed because it is too large Load Diff
@@ -32,3 +32,136 @@ func TestMeshLatestObservationKeyDefaults(t *testing.T) {
t.Fatalf("key = %q", key)
}
}
func TestEnrichVPNClientFabricRoutePrefersPlacementEntryAndActiveExit(t *testing.T) {
item := VPNClientConnection{
AllowedNodeIDs: []string{"node-a", "node-b", "node-b"},
EntryNodeIDs: []string{"entry-1", "entry-2"},
ExitNodeID: "exit-policy",
ActiveLease: &NodeVPNAssignmentLease{
OwnerNodeID: "exit-active",
},
ClientConfig: json.RawMessage(`{"routes":["0.0.0.0/0"]}`),
}
var cfg map[string]any
if err := json.Unmarshal(enrichVPNClientFabricRoute(item, "entry-2", ""), &cfg); err != nil {
t.Fatalf("unmarshal enriched config: %v", err)
}
route, ok := cfg["vpn_fabric_route"].(map[string]any)
if !ok {
t.Fatalf("missing vpn_fabric_route in %#v", cfg)
}
if route["preferred_data_plane"] != "fabric_mesh" || route["fallback_data_plane"] != "backend_relay" {
t.Fatalf("unexpected data-plane route contract: %#v", route)
}
if route["selected_entry_node_id"] != "entry-2" || route["selected_exit_node_id"] != "exit-active" {
t.Fatalf("unexpected selected route endpoints: %#v", route)
}
if route["route_candidate_count"].(float64) != 8 {
t.Fatalf("route candidate count = %#v", route["route_candidate_count"])
}
candidates := route["route_candidates"].([]any)
firstCandidate := candidates[0].(map[string]any)
if firstCandidate["role"] != "preferred" || firstCandidate["entry_node_id"] != "entry-2" || firstCandidate["exit_node_id"] != "exit-active" {
t.Fatalf("preferred route candidate = %#v", firstCandidate)
}
entryPool := route["entry_pool_node_ids"].([]any)
exitPool := route["exit_pool_node_ids"].([]any)
if len(entryPool) != 2 || entryPool[0] != "entry-1" || entryPool[1] != "entry-2" {
t.Fatalf("entry pool = %#v", entryPool)
}
if len(exitPool) != 4 || exitPool[0] != "exit-policy" || exitPool[1] != "exit-active" || exitPool[2] != "node-a" || exitPool[3] != "node-b" {
t.Fatalf("exit pool = %#v", exitPool)
}
contract, ok := cfg["vpn_dataplane_contract"].(map[string]any)
if !ok {
t.Fatalf("missing vpn_dataplane_contract in %#v", cfg)
}
if contract["tunnel_type"] != "universal_ip_packet" || contract["application_protocol_agnostic"] != true {
t.Fatalf("unexpected dataplane contract: %#v", contract)
}
failover := contract["failover"].(map[string]any)
if failover["enabled"] != true || failover["alternate_route_count"].(float64) != 7 {
t.Fatalf("unexpected failover contract: %#v", failover)
}
}
func TestEnrichVPNClientFabricRoutePrefersExplicitExit(t *testing.T) {
item := VPNClientConnection{
AllowedNodeIDs: []string{"node-a", "node-b", "node-c"},
EntryNodeIDs: []string{"entry-1", "entry-2"},
ExitNodeID: "exit-policy-a",
ActiveLease: &NodeVPNAssignmentLease{
OwnerNodeID: "",
},
ClientConfig: json.RawMessage(`{"routes":["0.0.0.0/0"]}`),
}
var cfg map[string]any
if err := json.Unmarshal(enrichVPNClientFabricRoute(item, "entry-1", "node-c"), &cfg); err != nil {
t.Fatalf("unmarshal enriched config: %v", err)
}
route, ok := cfg["vpn_fabric_route"].(map[string]any)
if !ok {
t.Fatalf("missing vpn_fabric_route in %#v", cfg)
}
if route["selected_entry_node_id"] != "entry-1" {
t.Fatalf("unexpected selected entry: %#v", route["selected_entry_node_id"])
}
if route["selected_exit_node_id"] != "node-c" {
t.Fatalf("unexpected selected exit: %#v", route["selected_exit_node_id"])
}
}
func TestEnrichVPNClientEntryEndpointCandidatesAddsReportedEntryAPI(t *testing.T) {
item := VPNClientConnection{
EntryNodeIDs: []string{"entry-1"},
ClientConfig: json.RawMessage(`{
"vpn_fabric_route": {
"status": "planned",
"selected_entry_node_id": "entry-1",
"selected_exit_node_id": "exit-1"
}
}`),
}
heartbeatMetadata := json.RawMessage(`{
"mesh_endpoint_report": {
"transport": "direct_http",
"connectivity_mode": "direct",
"nat_type": "none",
"region": "test",
"peer_endpoint": "http://entry.example.test:19131",
"endpoint_candidates": [{
"endpoint_id": "public-http",
"node_id": "entry-1",
"transport": "direct_http",
"address": "http://entry.example.test:19131",
"reachability": "public",
"priority": 0
}]
}
}`)
endpoints := map[string][]map[string]any{
"entry-1": vpnEntryEndpointCandidatesFromHeartbeat("entry-1", json.RawMessage(`{"vpn_local_gateway_shortcut":true}`), heartbeatMetadata),
}
var cfg map[string]any
if err := json.Unmarshal(enrichVPNClientEntryEndpointCandidates(item, endpoints), &cfg); err != nil {
t.Fatalf("unmarshal enriched config: %v", err)
}
if cfg["vpn_entry_endpoint_candidate_count"].(float64) != 1 {
t.Fatalf("candidate count = %#v", cfg["vpn_entry_endpoint_candidate_count"])
}
candidates := cfg["vpn_entry_endpoint_candidates"].([]any)
candidate := candidates[0].(map[string]any)
if candidate["node_id"] != "entry-1" || candidate["api_base_url"] != "http://entry.example.test:19131/api/v1" {
t.Fatalf("unexpected endpoint candidate: %#v", candidate)
}
if candidate["local_gateway_shortcut"] != true {
t.Fatalf("local gateway shortcut missing: %#v", candidate)
}
if candidate["selected_entry"] != true || candidate["source"] != "node_latest_heartbeat.mesh_endpoint_report.endpoint_candidates" {
t.Fatalf("unexpected endpoint metadata: %#v", candidate)
}
}
+28 -1
View File
@@ -22,6 +22,7 @@ type Repository interface {
AssignNodeToGroup(ctx context.Context, input AssignNodeGroupInput) (ClusterNode, error)
CreateJoinToken(ctx context.Context, input CreateJoinTokenInput, tokenHash string) (NodeJoinToken, error)
ListJoinTokens(ctx context.Context, clusterID string) ([]NodeJoinToken, error)
SetJoinTokenAuthority(ctx context.Context, clusterID, tokenID string, payload json.RawMessage, signature ClusterSignature) (NodeJoinToken, error)
GetValidJoinTokenByHash(ctx context.Context, clusterID, tokenHash string) (NodeJoinToken, error)
RevokeJoinToken(ctx context.Context, input RevokeJoinTokenInput) (NodeJoinToken, error)
@@ -40,8 +41,16 @@ type Repository interface {
RecordHeartbeat(ctx context.Context, input RecordHeartbeatInput) (NodeHeartbeat, error)
ListNodeHeartbeats(ctx context.Context, clusterID, nodeID string, limit int) ([]NodeHeartbeat, error)
CreateReleaseVersion(ctx context.Context, input CreateReleaseVersionInput) (ReleaseVersion, error)
ListReleaseVersions(ctx context.Context, clusterID, product, channel string) ([]ReleaseVersion, error)
ListNodeUpdateServiceCandidates(ctx context.Context, clusterID string) ([]NodeUpdateServiceCandidate, error)
UpsertNodeUpdatePolicy(ctx context.Context, input UpsertNodeUpdatePolicyInput) (NodeUpdatePolicy, error)
GetNodeUpdatePolicy(ctx context.Context, clusterID, nodeID, product string) (NodeUpdatePolicy, error)
ReportNodeUpdateStatus(ctx context.Context, input ReportNodeUpdateStatusInput) (NodeUpdateStatus, error)
ListNodeUpdateStatuses(ctx context.Context, clusterID, nodeID string, limit int) ([]NodeUpdateStatus, error)
RevokeNodeIdentity(ctx context.Context, input RevokeNodeIdentityInput) error
DisableClusterMembership(ctx context.Context, input DisableMembershipInput) error
DeleteClusterNode(ctx context.Context, input DeleteClusterNodeInput) error
UpsertFabricTestingFlag(ctx context.Context, input UpsertFabricTestingFlagInput) (FabricTestingFlag, error)
ListFabricTestingFlags(ctx context.Context) ([]FabricTestingFlag, error)
GetEffectiveNodeTestingFlags(ctx context.Context, clusterID, nodeID string) (EffectiveNodeTestingFlags, error)
@@ -55,6 +64,22 @@ type Repository interface {
ListMeshLinks(ctx context.Context, clusterID string) ([]MeshLinkObservation, error)
CreateRouteIntent(ctx context.Context, input CreateRouteIntentInput) (MeshRouteIntent, error)
ListRouteIntents(ctx context.Context, clusterID string) ([]MeshRouteIntent, error)
ExpireRouteIntent(ctx context.Context, input RouteIntentLifecycleInput, expiresAt time.Time) (MeshRouteIntent, error)
DisableRouteIntent(ctx context.Context, input RouteIntentLifecycleInput) (MeshRouteIntent, error)
RecordFabricServiceChannelRouteFeedback(ctx context.Context, input RecordFabricServiceChannelRouteFeedbackInput) (FabricServiceChannelRouteFeedbackObservation, error)
ListFabricServiceChannelRouteFeedback(ctx context.Context, input ListFabricServiceChannelRouteFeedbackInput) ([]FabricServiceChannelRouteFeedbackObservation, error)
ExpireFabricServiceChannelRouteFeedback(ctx context.Context, input ExpireFabricServiceChannelRouteFeedbackInput) (ExpireFabricServiceChannelRouteFeedbackResult, error)
StoreFabricServiceChannelLease(ctx context.Context, input StoreFabricServiceChannelLeaseInput) (FabricServiceChannelLeaseRecord, error)
GetFabricServiceChannelLease(ctx context.Context, clusterID, channelID string) (FabricServiceChannelLeaseRecord, error)
ListFabricServiceChannelLeases(ctx context.Context, input ListFabricServiceChannelLeasesInput) ([]FabricServiceChannelLeaseRecord, error)
CleanupExpiredFabricServiceChannelLeases(ctx context.Context, clusterID string, now time.Time, limit int) (int, error)
RecordFabricServiceChannelRouteRebuildAttempt(ctx context.Context, input RecordFabricServiceChannelRouteRebuildAttemptInput) (FabricServiceChannelRouteRebuildAttempt, error)
ListFabricServiceChannelRouteRebuildAttempts(ctx context.Context, input ListFabricServiceChannelRouteRebuildAttemptsInput) ([]FabricServiceChannelRouteRebuildAttempt, error)
UpdateFabricServiceChannelRouteRebuildCorrelationSnapshot(ctx context.Context, input UpdateFabricServiceChannelRouteRebuildCorrelationSnapshotInput) error
GetFabricServiceChannelSchemaStatus(ctx context.Context, input GetFabricServiceChannelSchemaStatusInput) (FabricServiceChannelSchemaStatus, error)
UpsertFabricServiceChannelRouteRebuildAlertSilence(ctx context.Context, input SilenceFabricServiceChannelRouteRebuildAlertInput, expiresAt time.Time) (FabricServiceChannelRouteRebuildAlertSilence, error)
ListFabricServiceChannelRouteRebuildAlertSilences(ctx context.Context, clusterID string, now time.Time) ([]FabricServiceChannelRouteRebuildAlertSilence, error)
DeleteFabricServiceChannelRouteRebuildAlertSilence(ctx context.Context, input UnsilenceFabricServiceChannelRouteRebuildAlertInput) (FabricServiceChannelRouteRebuildAlertSilence, error)
ListQoSPolicies(ctx context.Context, clusterID string) ([]MeshQoSPolicy, error)
ListFabricEntryPoints(ctx context.Context, clusterID string) ([]FabricEntryPoint, error)
CreateFabricEntryPoint(ctx context.Context, input CreateFabricEntryPointInput) (FabricEntryPoint, error)
@@ -78,6 +103,7 @@ type Repository interface {
ListVPNConnectionAllowedNodes(ctx context.Context, clusterID, vpnConnectionID string) ([]VPNConnectionAllowedNode, error)
AcquireVPNConnectionLease(ctx context.Context, input AcquireVPNConnectionLeaseInput, expiresAt time.Time, fencingToken string) (VPNConnectionLease, error)
RenewVPNConnectionLease(ctx context.Context, input RenewVPNConnectionLeaseInput, expiresAt time.Time) (VPNConnectionLease, error)
RenewNodeVPNAssignmentLease(ctx context.Context, input RenewNodeVPNAssignmentLeaseInput, expiresAt time.Time) (VPNConnectionLease, error)
ReleaseVPNConnectionLease(ctx context.Context, input ReleaseVPNConnectionLeaseInput) (VPNConnectionLease, error)
FenceVPNConnectionLease(ctx context.Context, input FenceVPNConnectionLeaseInput) (VPNConnectionLease, error)
GetActiveVPNConnectionLease(ctx context.Context, clusterID, vpnConnectionID string) (VPNConnectionLease, error)
@@ -85,7 +111,8 @@ type Repository interface {
ExpireStaleVPNConnectionLeases(ctx context.Context, clusterID string, now time.Time) ([]VPNConnectionLease, error)
ListNodeVPNAssignments(ctx context.Context, clusterID, nodeID string) ([]NodeVPNAssignment, error)
ReportNodeVPNAssignmentStatus(ctx context.Context, input ReportNodeVPNAssignmentStatusInput) (NodeVPNAssignmentStatus, error)
GetVPNClientProfile(ctx context.Context, clusterID, organizationID, userID, preferredEntryNodeID, preferredExitNodeID string, generatedAt time.Time) (VPNClientProfile, error)
RecordAudit(ctx context.Context, event ClusterAuditEvent) error
ListAuditEvents(ctx context.Context, clusterID string, limit int) ([]ClusterAuditEvent, error)
ListAuditEvents(ctx context.Context, input ListAuditEventsInput) ([]ClusterAuditEvent, error)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff