The agent connects to mc-proxy via Unix socket and automatically registers/removes routes during deploy and stop. This eliminates manual mcproxyctl usage or TOML editing. - New ProxyRouter abstraction wraps mc-proxy client library - Deploy: after container starts, registers routes with mc-proxy using host ports from the registry - Stop: removes routes from mc-proxy before stopping container - Config: [mcproxy] section with socket path and cert_dir - Nil-safe: if mc-proxy socket not configured, route registration is silently skipped (backward compatible) - L7 routes use certs from convention path (<cert_dir>/<service>.pem) - L4 routes use TLS passthrough (backend_tls=true) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"path/filepath"
|
|
|
|
"git.wntrmute.dev/kyle/mc-proxy/client/mcproxy"
|
|
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
|
)
|
|
|
|
// ProxyRouter registers and removes routes with mc-proxy.
|
|
// If the mc-proxy socket is not configured, it logs and returns nil
|
|
// (route registration is optional).
|
|
type ProxyRouter struct {
|
|
client *mcproxy.Client
|
|
certDir string
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// NewProxyRouter connects to mc-proxy via Unix socket. Returns nil
|
|
// if socketPath is empty (route registration disabled).
|
|
func NewProxyRouter(socketPath, certDir string, logger *slog.Logger) (*ProxyRouter, error) {
|
|
if socketPath == "" {
|
|
logger.Info("mc-proxy socket not configured, route registration disabled")
|
|
return nil, nil
|
|
}
|
|
|
|
client, err := mcproxy.Dial(socketPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("connect to mc-proxy at %s: %w", socketPath, err)
|
|
}
|
|
|
|
logger.Info("connected to mc-proxy", "socket", socketPath)
|
|
return &ProxyRouter{
|
|
client: client,
|
|
certDir: certDir,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the mc-proxy connection.
|
|
func (p *ProxyRouter) Close() error {
|
|
if p == nil || p.client == nil {
|
|
return nil
|
|
}
|
|
return p.client.Close()
|
|
}
|
|
|
|
// RegisterRoutes registers all routes for a service component with mc-proxy.
|
|
// It uses the assigned host ports from the registry.
|
|
func (p *ProxyRouter) RegisterRoutes(ctx context.Context, serviceName string, routes []registry.Route, hostPorts map[string]int) error {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, r := range routes {
|
|
hostPort, ok := hostPorts[r.Name]
|
|
if !ok || hostPort == 0 {
|
|
continue
|
|
}
|
|
|
|
hostname := r.Hostname
|
|
if hostname == "" {
|
|
hostname = serviceName + ".svc.mcp.metacircular.net"
|
|
}
|
|
|
|
listenerAddr := listenerForMode(r.Mode, r.Port)
|
|
backend := fmt.Sprintf("127.0.0.1:%d", hostPort)
|
|
|
|
route := mcproxy.Route{
|
|
Hostname: hostname,
|
|
Backend: backend,
|
|
Mode: r.Mode,
|
|
BackendTLS: r.Mode == "l4", // L4 passthrough: backend handles TLS. L7: mc-proxy terminates.
|
|
}
|
|
|
|
// L7 routes need TLS cert/key for mc-proxy to terminate TLS.
|
|
if r.Mode == "l7" {
|
|
route.TLSCert = filepath.Join(p.certDir, serviceName+".pem")
|
|
route.TLSKey = filepath.Join(p.certDir, serviceName+".key")
|
|
}
|
|
|
|
p.logger.Info("registering route",
|
|
"service", serviceName,
|
|
"hostname", hostname,
|
|
"listener", listenerAddr,
|
|
"backend", backend,
|
|
"mode", r.Mode,
|
|
)
|
|
|
|
if err := p.client.AddRoute(ctx, listenerAddr, route); err != nil {
|
|
return fmt.Errorf("register route %s on %s: %w", hostname, listenerAddr, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveRoutes removes all routes for a service component from mc-proxy.
|
|
func (p *ProxyRouter) RemoveRoutes(ctx context.Context, serviceName string, routes []registry.Route) error {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, r := range routes {
|
|
hostname := r.Hostname
|
|
if hostname == "" {
|
|
hostname = serviceName + ".svc.mcp.metacircular.net"
|
|
}
|
|
|
|
listenerAddr := listenerForMode(r.Mode, r.Port)
|
|
|
|
p.logger.Info("removing route",
|
|
"service", serviceName,
|
|
"hostname", hostname,
|
|
"listener", listenerAddr,
|
|
)
|
|
|
|
if err := p.client.RemoveRoute(ctx, listenerAddr, hostname); err != nil {
|
|
// Log but don't fail — the route may already be gone.
|
|
p.logger.Warn("failed to remove route",
|
|
"hostname", hostname,
|
|
"listener", listenerAddr,
|
|
"err", err,
|
|
)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// listenerForMode returns the mc-proxy listener address for a given
|
|
// route mode and external port.
|
|
func listenerForMode(mode string, port int) string {
|
|
return fmt.Sprintf(":%d", port)
|
|
}
|