1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! Overlay network API DTOs.
//!
//! Wire types for the `/api/v1/overlay/*` HTTP surface, shared between
//! `zlayer-api` (server) and `zlayer-client` / `zlayer-manager` (clients).
//! These are the response shapes consumed by both the daemon and SDK
//! clients.
//!
//! See [`crate::overlay`] for the underlying configuration types.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::IpAddr;
use utoipa::ToSchema;
use crate::overlay::OverlayMode;
/// Where on a specific node a service's overlay terminates.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
pub struct BridgeInfo {
/// Linux bridge name (`br-svc-<hash>`), max 15 chars per IFNAMSIZ.
pub name: String,
/// CIDR of the service subnet assigned to this node.
#[schema(value_type = String, example = "10.201.0.0/24")]
pub subnet: ipnet::IpNet,
/// Gateway IP within the subnet (first usable address); the bridge's
/// own L3 address. Containers' default route points here.
#[schema(value_type = String, example = "10.201.0.1")]
pub gateway: IpAddr,
/// Node identifier (stringified to avoid leaking `NodeId` type details
/// across crate boundaries).
pub node_id: String,
}
/// Status of a service's overlay across the cluster.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
pub struct ServiceOverlayStatus {
pub service: String,
/// Mode the daemon resolved for this service (after `resolve`).
pub mode: OverlayMode,
/// One entry per node where this service has at least one container
/// attached. Keys are node IDs as stringified.
pub bridges_by_node: HashMap<String, BridgeInfo>,
}
/// Overlay network status response
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct OverlayStatusResponse {
/// Overlay interface name
pub interface: String,
/// Whether this node is the cluster leader
pub is_leader: bool,
/// Node's overlay IP address
pub node_ip: String,
/// Overlay network CIDR
pub cidr: String,
/// Overlay listen port (`WireGuard` protocol)
pub port: u16,
/// Total number of peers
pub total_peers: usize,
/// Number of healthy peers
pub healthy_peers: usize,
/// Number of unhealthy peers
pub unhealthy_peers: usize,
/// Last health check timestamp (unix epoch seconds)
pub last_check: u64,
}
/// Peer information
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct PeerInfo {
/// Peer's public key
pub public_key: String,
/// Peer's overlay IP address
#[serde(skip_serializing_if = "Option::is_none")]
pub overlay_ip: Option<String>,
/// Whether the peer is healthy
pub healthy: bool,
/// Seconds since last handshake
#[serde(skip_serializing_if = "Option::is_none")]
pub last_handshake_secs: Option<u64>,
/// Last ping latency in milliseconds
#[serde(skip_serializing_if = "Option::is_none")]
pub last_ping_ms: Option<u64>,
/// Number of consecutive health check failures
pub failure_count: u32,
/// Last health check timestamp (unix epoch seconds)
pub last_check: u64,
}
/// Peer list response
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct PeerListResponse {
/// Total number of peers
pub total: usize,
/// Number of healthy peers
pub healthy: usize,
/// List of peer information
pub peers: Vec<PeerInfo>,
}
/// IP allocation status response
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct IpAllocationResponse {
/// Overlay network CIDR
pub cidr: String,
/// Total available IPs in the range
pub total_ips: u32,
/// Number of allocated IPs
pub allocated_count: usize,
/// Number of available IPs
pub available_count: u32,
/// Utilization percentage (0.0 - 100.0)
pub utilization_percent: f64,
/// List of allocated IP addresses (only included if requested)
#[serde(skip_serializing_if = "Option::is_none")]
pub allocated_ips: Option<Vec<String>>,
}
/// NAT traversal status response
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct NatStatusResponse {
/// Whether NAT traversal is enabled in the daemon's config
pub enabled: bool,
/// Configured STUN servers (host:port)
pub stun_servers: Vec<String>,
/// Configured TURN/relay servers (host:port)
pub turn_servers: Vec<String>,
/// Address of the locally-bound built-in relay server, if running
#[serde(skip_serializing_if = "Option::is_none")]
pub relay_server_bind: Option<String>,
/// Locally gathered ICE candidates
pub candidates: Vec<NatCandidateDto>,
/// Per-peer NAT connectivity state
pub peers: Vec<NatPeerDto>,
/// Unix epoch seconds of the last successful STUN refresh
pub last_refresh: u64,
}
/// Locally gathered NAT candidate (`Host` / `ServerReflexive` / `Relay`).
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct NatCandidateDto {
/// `Host` / `ServerReflexive` / `Relay`
pub kind: String,
/// Transport (e.g. "udp")
pub transport: String,
/// Address (host:port)
pub address: String,
/// Priority (higher = preferred)
pub priority: u32,
}
/// Per-peer NAT connectivity entry.
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct NatPeerDto {
/// Peer node ID
pub node_id: String,
/// Direct / `HolePunched` / Relayed / Unreachable
pub connection_type: String,
/// Selected remote endpoint, if any
#[serde(skip_serializing_if = "Option::is_none")]
pub remote_endpoint: Option<String>,
}
/// DNS service status response
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct DnsStatusResponse {
/// Whether DNS service is enabled
pub enabled: bool,
/// DNS zone name
#[serde(skip_serializing_if = "Option::is_none")]
pub zone: Option<String>,
/// DNS server port
#[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
/// DNS server bind address
#[serde(skip_serializing_if = "Option::is_none")]
pub bind_addr: Option<String>,
/// Number of registered services
pub service_count: usize,
/// List of registered service names
pub services: Vec<String>,
}