Add per-route details to status, move socket to /srv/mc-proxy/
mcproxyctl status now shows individual routes per listener with hostname, backend, mode, and re-encrypt indicator. Proto, gRPC server, client library, and CLI all updated. Default gRPC socket path moved from /var/run/mc-proxy.sock to /srv/mc-proxy/mc-proxy.sock to match the service data directory convention. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -487,7 +487,7 @@ addr = ":8443"
|
||||
# gRPC admin API. Optional -- omit or leave addr empty to disable.
|
||||
# Listens on a Unix socket; access controlled via filesystem permissions.
|
||||
[grpc]
|
||||
addr = "/var/run/mc-proxy.sock"
|
||||
addr = "/srv/mc-proxy/mc-proxy.sock"
|
||||
|
||||
# Firewall. Global blocklist, evaluated before routing. Default allow.
|
||||
[firewall]
|
||||
|
||||
@@ -162,6 +162,15 @@ func (c *Client) RemoveFirewallRule(ctx context.Context, ruleType FirewallRuleTy
|
||||
return err
|
||||
}
|
||||
|
||||
// RouteStatus contains status information for a single route.
|
||||
type RouteStatus struct {
|
||||
Hostname string
|
||||
Backend string
|
||||
Mode string // "l4" or "l7"
|
||||
BackendTLS bool
|
||||
SendProxyProtocol bool
|
||||
}
|
||||
|
||||
// ListenerStatus contains status information for a single listener.
|
||||
type ListenerStatus struct {
|
||||
Addr string
|
||||
@@ -169,6 +178,7 @@ type ListenerStatus struct {
|
||||
ActiveConnections int64
|
||||
ProxyProtocol bool
|
||||
MaxConnections int64
|
||||
Routes []RouteStatus
|
||||
}
|
||||
|
||||
// Status contains the server's current status.
|
||||
@@ -196,12 +206,23 @@ func (c *Client) GetStatus(ctx context.Context) (*Status, error) {
|
||||
|
||||
status.Listeners = make([]ListenerStatus, len(resp.Listeners))
|
||||
for i, ls := range resp.Listeners {
|
||||
routes := make([]RouteStatus, len(ls.Routes))
|
||||
for j, r := range ls.Routes {
|
||||
routes[j] = RouteStatus{
|
||||
Hostname: r.Hostname,
|
||||
Backend: r.Backend,
|
||||
Mode: r.Mode,
|
||||
BackendTLS: r.BackendTls,
|
||||
SendProxyProtocol: r.SendProxyProtocol,
|
||||
}
|
||||
}
|
||||
status.Listeners[i] = ListenerStatus{
|
||||
Addr: ls.Addr,
|
||||
RouteCount: int(ls.RouteCount),
|
||||
ActiveConnections: ls.ActiveConnections,
|
||||
ProxyProtocol: ls.ProxyProtocol,
|
||||
MaxConnections: ls.MaxConnections,
|
||||
Routes: routes,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
// # Basic Usage
|
||||
//
|
||||
// client, err := mcproxy.Dial("/var/run/mc-proxy.sock")
|
||||
// client, err := mcproxy.Dial("/srv/mc-proxy/mc-proxy.sock")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"git.wntrmute.dev/kyle/mc-proxy/client/mcproxy"
|
||||
)
|
||||
|
||||
const defaultSocketPath = "/var/run/mc-proxy.sock"
|
||||
const defaultSocketPath = "/srv/mc-proxy/mc-proxy.sock"
|
||||
|
||||
type contextKey string
|
||||
|
||||
|
||||
@@ -34,6 +34,17 @@ func statusCmd() *cobra.Command {
|
||||
|
||||
for _, ls := range status.Listeners {
|
||||
fmt.Printf(" %s routes=%d active=%d\n", ls.Addr, ls.RouteCount, ls.ActiveConnections)
|
||||
for _, r := range ls.Routes {
|
||||
mode := r.Mode
|
||||
if mode == "" {
|
||||
mode = "l4"
|
||||
}
|
||||
extra := ""
|
||||
if r.BackendTLS {
|
||||
extra = " (re-encrypt)"
|
||||
}
|
||||
fmt.Printf(" %s %s → %s%s\n", mode, r.Hostname, r.Backend, extra)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v5.29.5
|
||||
// protoc v6.32.1
|
||||
// source: proto/mc_proxy/v1/admin.proto
|
||||
|
||||
package mcproxyv1
|
||||
@@ -1173,6 +1173,7 @@ type ListenerStatus struct {
|
||||
ActiveConnections int64 `protobuf:"varint,3,opt,name=active_connections,json=activeConnections,proto3" json:"active_connections,omitempty"`
|
||||
ProxyProtocol bool `protobuf:"varint,4,opt,name=proxy_protocol,json=proxyProtocol,proto3" json:"proxy_protocol,omitempty"`
|
||||
MaxConnections int64 `protobuf:"varint,5,opt,name=max_connections,json=maxConnections,proto3" json:"max_connections,omitempty"`
|
||||
Routes []*Route `protobuf:"bytes,6,rep,name=routes,proto3" json:"routes,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -1242,6 +1243,13 @@ func (x *ListenerStatus) GetMaxConnections() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ListenerStatus) GetRoutes() []*Route {
|
||||
if x != nil {
|
||||
return x.Routes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetStatusRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@@ -1408,14 +1416,15 @@ const file_proto_mc_proxy_v1_admin_proto_rawDesc = "" +
|
||||
" SetListenerMaxConnectionsRequest\x12#\n" +
|
||||
"\rlistener_addr\x18\x01 \x01(\tR\flistenerAddr\x12'\n" +
|
||||
"\x0fmax_connections\x18\x02 \x01(\x03R\x0emaxConnections\"#\n" +
|
||||
"!SetListenerMaxConnectionsResponse\"\xc4\x01\n" +
|
||||
"!SetListenerMaxConnectionsResponse\"\xf0\x01\n" +
|
||||
"\x0eListenerStatus\x12\x12\n" +
|
||||
"\x04addr\x18\x01 \x01(\tR\x04addr\x12\x1f\n" +
|
||||
"\vroute_count\x18\x02 \x01(\x05R\n" +
|
||||
"routeCount\x12-\n" +
|
||||
"\x12active_connections\x18\x03 \x01(\x03R\x11activeConnections\x12%\n" +
|
||||
"\x0eproxy_protocol\x18\x04 \x01(\bR\rproxyProtocol\x12'\n" +
|
||||
"\x0fmax_connections\x18\x05 \x01(\x03R\x0emaxConnections\"\x12\n" +
|
||||
"\x0fmax_connections\x18\x05 \x01(\x03R\x0emaxConnections\x12*\n" +
|
||||
"\x06routes\x18\x06 \x03(\v2\x12.mc_proxy.v1.RouteR\x06routes\"\x12\n" +
|
||||
"\x10GetStatusRequest\"\xd0\x01\n" +
|
||||
"\x11GetStatusResponse\x12\x18\n" +
|
||||
"\aversion\x18\x01 \x01(\tR\aversion\x129\n" +
|
||||
@@ -1497,35 +1506,36 @@ var file_proto_mc_proxy_v1_admin_proto_depIdxs = []int32{
|
||||
15, // 7: mc_proxy.v1.GetFirewallRulesResponse.rules:type_name -> mc_proxy.v1.FirewallRule
|
||||
15, // 8: mc_proxy.v1.AddFirewallRuleRequest.rule:type_name -> mc_proxy.v1.FirewallRule
|
||||
15, // 9: mc_proxy.v1.RemoveFirewallRuleRequest.rule:type_name -> mc_proxy.v1.FirewallRule
|
||||
27, // 10: mc_proxy.v1.GetStatusResponse.started_at:type_name -> google.protobuf.Timestamp
|
||||
24, // 11: mc_proxy.v1.GetStatusResponse.listeners:type_name -> mc_proxy.v1.ListenerStatus
|
||||
3, // 12: mc_proxy.v1.ProxyAdminService.ListRoutes:input_type -> mc_proxy.v1.ListRoutesRequest
|
||||
5, // 13: mc_proxy.v1.ProxyAdminService.AddRoute:input_type -> mc_proxy.v1.AddRouteRequest
|
||||
7, // 14: mc_proxy.v1.ProxyAdminService.RemoveRoute:input_type -> mc_proxy.v1.RemoveRouteRequest
|
||||
16, // 15: mc_proxy.v1.ProxyAdminService.GetFirewallRules:input_type -> mc_proxy.v1.GetFirewallRulesRequest
|
||||
18, // 16: mc_proxy.v1.ProxyAdminService.AddFirewallRule:input_type -> mc_proxy.v1.AddFirewallRuleRequest
|
||||
20, // 17: mc_proxy.v1.ProxyAdminService.RemoveFirewallRule:input_type -> mc_proxy.v1.RemoveFirewallRuleRequest
|
||||
22, // 18: mc_proxy.v1.ProxyAdminService.SetListenerMaxConnections:input_type -> mc_proxy.v1.SetListenerMaxConnectionsRequest
|
||||
9, // 19: mc_proxy.v1.ProxyAdminService.ListL7Policies:input_type -> mc_proxy.v1.ListL7PoliciesRequest
|
||||
11, // 20: mc_proxy.v1.ProxyAdminService.AddL7Policy:input_type -> mc_proxy.v1.AddL7PolicyRequest
|
||||
13, // 21: mc_proxy.v1.ProxyAdminService.RemoveL7Policy:input_type -> mc_proxy.v1.RemoveL7PolicyRequest
|
||||
25, // 22: mc_proxy.v1.ProxyAdminService.GetStatus:input_type -> mc_proxy.v1.GetStatusRequest
|
||||
4, // 23: mc_proxy.v1.ProxyAdminService.ListRoutes:output_type -> mc_proxy.v1.ListRoutesResponse
|
||||
6, // 24: mc_proxy.v1.ProxyAdminService.AddRoute:output_type -> mc_proxy.v1.AddRouteResponse
|
||||
8, // 25: mc_proxy.v1.ProxyAdminService.RemoveRoute:output_type -> mc_proxy.v1.RemoveRouteResponse
|
||||
17, // 26: mc_proxy.v1.ProxyAdminService.GetFirewallRules:output_type -> mc_proxy.v1.GetFirewallRulesResponse
|
||||
19, // 27: mc_proxy.v1.ProxyAdminService.AddFirewallRule:output_type -> mc_proxy.v1.AddFirewallRuleResponse
|
||||
21, // 28: mc_proxy.v1.ProxyAdminService.RemoveFirewallRule:output_type -> mc_proxy.v1.RemoveFirewallRuleResponse
|
||||
23, // 29: mc_proxy.v1.ProxyAdminService.SetListenerMaxConnections:output_type -> mc_proxy.v1.SetListenerMaxConnectionsResponse
|
||||
10, // 30: mc_proxy.v1.ProxyAdminService.ListL7Policies:output_type -> mc_proxy.v1.ListL7PoliciesResponse
|
||||
12, // 31: mc_proxy.v1.ProxyAdminService.AddL7Policy:output_type -> mc_proxy.v1.AddL7PolicyResponse
|
||||
14, // 32: mc_proxy.v1.ProxyAdminService.RemoveL7Policy:output_type -> mc_proxy.v1.RemoveL7PolicyResponse
|
||||
26, // 33: mc_proxy.v1.ProxyAdminService.GetStatus:output_type -> mc_proxy.v1.GetStatusResponse
|
||||
23, // [23:34] is the sub-list for method output_type
|
||||
12, // [12:23] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
2, // 10: mc_proxy.v1.ListenerStatus.routes:type_name -> mc_proxy.v1.Route
|
||||
27, // 11: mc_proxy.v1.GetStatusResponse.started_at:type_name -> google.protobuf.Timestamp
|
||||
24, // 12: mc_proxy.v1.GetStatusResponse.listeners:type_name -> mc_proxy.v1.ListenerStatus
|
||||
3, // 13: mc_proxy.v1.ProxyAdminService.ListRoutes:input_type -> mc_proxy.v1.ListRoutesRequest
|
||||
5, // 14: mc_proxy.v1.ProxyAdminService.AddRoute:input_type -> mc_proxy.v1.AddRouteRequest
|
||||
7, // 15: mc_proxy.v1.ProxyAdminService.RemoveRoute:input_type -> mc_proxy.v1.RemoveRouteRequest
|
||||
16, // 16: mc_proxy.v1.ProxyAdminService.GetFirewallRules:input_type -> mc_proxy.v1.GetFirewallRulesRequest
|
||||
18, // 17: mc_proxy.v1.ProxyAdminService.AddFirewallRule:input_type -> mc_proxy.v1.AddFirewallRuleRequest
|
||||
20, // 18: mc_proxy.v1.ProxyAdminService.RemoveFirewallRule:input_type -> mc_proxy.v1.RemoveFirewallRuleRequest
|
||||
22, // 19: mc_proxy.v1.ProxyAdminService.SetListenerMaxConnections:input_type -> mc_proxy.v1.SetListenerMaxConnectionsRequest
|
||||
9, // 20: mc_proxy.v1.ProxyAdminService.ListL7Policies:input_type -> mc_proxy.v1.ListL7PoliciesRequest
|
||||
11, // 21: mc_proxy.v1.ProxyAdminService.AddL7Policy:input_type -> mc_proxy.v1.AddL7PolicyRequest
|
||||
13, // 22: mc_proxy.v1.ProxyAdminService.RemoveL7Policy:input_type -> mc_proxy.v1.RemoveL7PolicyRequest
|
||||
25, // 23: mc_proxy.v1.ProxyAdminService.GetStatus:input_type -> mc_proxy.v1.GetStatusRequest
|
||||
4, // 24: mc_proxy.v1.ProxyAdminService.ListRoutes:output_type -> mc_proxy.v1.ListRoutesResponse
|
||||
6, // 25: mc_proxy.v1.ProxyAdminService.AddRoute:output_type -> mc_proxy.v1.AddRouteResponse
|
||||
8, // 26: mc_proxy.v1.ProxyAdminService.RemoveRoute:output_type -> mc_proxy.v1.RemoveRouteResponse
|
||||
17, // 27: mc_proxy.v1.ProxyAdminService.GetFirewallRules:output_type -> mc_proxy.v1.GetFirewallRulesResponse
|
||||
19, // 28: mc_proxy.v1.ProxyAdminService.AddFirewallRule:output_type -> mc_proxy.v1.AddFirewallRuleResponse
|
||||
21, // 29: mc_proxy.v1.ProxyAdminService.RemoveFirewallRule:output_type -> mc_proxy.v1.RemoveFirewallRuleResponse
|
||||
23, // 30: mc_proxy.v1.ProxyAdminService.SetListenerMaxConnections:output_type -> mc_proxy.v1.SetListenerMaxConnectionsResponse
|
||||
10, // 31: mc_proxy.v1.ProxyAdminService.ListL7Policies:output_type -> mc_proxy.v1.ListL7PoliciesResponse
|
||||
12, // 32: mc_proxy.v1.ProxyAdminService.AddL7Policy:output_type -> mc_proxy.v1.AddL7PolicyResponse
|
||||
14, // 33: mc_proxy.v1.ProxyAdminService.RemoveL7Policy:output_type -> mc_proxy.v1.RemoveL7PolicyResponse
|
||||
26, // 34: mc_proxy.v1.ProxyAdminService.GetStatus:output_type -> mc_proxy.v1.GetStatusResponse
|
||||
24, // [24:35] is the sub-list for method output_type
|
||||
13, // [13:24] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_mc_proxy_v1_admin_proto_init() }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc v5.29.5
|
||||
// - protoc v6.32.1
|
||||
// source: proto/mc_proxy/v1/admin.proto
|
||||
|
||||
package mcproxyv1
|
||||
|
||||
@@ -39,7 +39,7 @@ type Database struct {
|
||||
|
||||
// GRPC holds the gRPC admin API configuration.
|
||||
type GRPC struct {
|
||||
Addr string `toml:"addr"` // Unix socket path (e.g., "/var/run/mc-proxy.sock")
|
||||
Addr string `toml:"addr"` // Unix socket path (e.g., "/srv/mc-proxy/mc-proxy.sock")
|
||||
}
|
||||
|
||||
// Listener is a proxy listener with its routes.
|
||||
@@ -218,7 +218,7 @@ func (c *Config) validate() error {
|
||||
if c.GRPC.Addr != "" {
|
||||
socketPath := c.GRPC.SocketPath()
|
||||
if !strings.Contains(socketPath, "/") {
|
||||
return fmt.Errorf("grpc.addr must be a Unix socket path (e.g., /var/run/mc-proxy.sock)")
|
||||
return fmt.Errorf("grpc.addr must be a Unix socket path (e.g., /srv/mc-proxy/mc-proxy.sock)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -200,8 +200,8 @@ func TestGRPCSocketPath(t *testing.T) {
|
||||
addr string
|
||||
want string
|
||||
}{
|
||||
{"/var/run/mc-proxy.sock", "/var/run/mc-proxy.sock"},
|
||||
{"unix:/var/run/mc-proxy.sock", "/var/run/mc-proxy.sock"},
|
||||
{"/srv/mc-proxy/mc-proxy.sock", "/srv/mc-proxy/mc-proxy.sock"},
|
||||
{"unix:/srv/mc-proxy/mc-proxy.sock", "/srv/mc-proxy/mc-proxy.sock"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
g := GRPC{Addr: tt.addr}
|
||||
@@ -220,7 +220,7 @@ func TestValidateGRPCUnixSocket(t *testing.T) {
|
||||
path = "/tmp/test.db"
|
||||
|
||||
[grpc]
|
||||
addr = "/var/run/mc-proxy.sock"
|
||||
addr = "/srv/mc-proxy/mc-proxy.sock"
|
||||
`
|
||||
if err := os.WriteFile(path, []byte(data), 0600); err != nil {
|
||||
t.Fatalf("write config: %v", err)
|
||||
|
||||
@@ -434,12 +434,23 @@ func (a *AdminServer) GetStatus(_ context.Context, _ *pb.GetStatusRequest) (*pb.
|
||||
var listeners []*pb.ListenerStatus
|
||||
for _, ls := range a.srv.Listeners() {
|
||||
routes := ls.Routes()
|
||||
var pbRoutes []*pb.Route
|
||||
for hostname, route := range routes {
|
||||
pbRoutes = append(pbRoutes, &pb.Route{
|
||||
Hostname: hostname,
|
||||
Backend: route.Backend,
|
||||
Mode: route.Mode,
|
||||
BackendTls: route.BackendTLS,
|
||||
SendProxyProtocol: route.SendProxyProtocol,
|
||||
})
|
||||
}
|
||||
listeners = append(listeners, &pb.ListenerStatus{
|
||||
Addr: ls.Addr,
|
||||
RouteCount: int32(len(routes)),
|
||||
ActiveConnections: ls.ActiveConnections.Load(),
|
||||
ProxyProtocol: ls.ProxyProtocol,
|
||||
MaxConnections: ls.MaxConnections,
|
||||
Routes: pbRoutes,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
BIN
mcproxyctl
BIN
mcproxyctl
Binary file not shown.
@@ -144,6 +144,7 @@ message ListenerStatus {
|
||||
int64 active_connections = 3;
|
||||
bool proxy_protocol = 4;
|
||||
int64 max_connections = 5;
|
||||
repeated Route routes = 6;
|
||||
}
|
||||
|
||||
message GetStatusRequest {}
|
||||
|
||||
Reference in New Issue
Block a user