Files
mcp/internal/masterdb/edge_routes.go
Kyle Isom 3c0b55f9f8 Add master database with nodes, placements, and edge_routes
New internal/masterdb/ package for mcp-master cluster state. Separate
from the agent's registry because the schemas are fundamentally
different (cluster-wide placement vs node-local containers).

Tables: nodes, placements, edge_routes. Full CRUD with tests.
Follows the same Open/migrate pattern as internal/registry/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:26:04 -07:00

96 lines
3.0 KiB
Go

package masterdb
import (
"database/sql"
"fmt"
"time"
)
// EdgeRoute records a public route managed by the master.
type EdgeRoute struct {
Hostname string
ServiceName string
EdgeNode string
BackendHostname string
BackendPort int
CreatedAt time.Time
}
// CreateEdgeRoute inserts or replaces an edge route record.
func CreateEdgeRoute(db *sql.DB, hostname, serviceName, edgeNode, backendHostname string, backendPort int) error {
_, err := db.Exec(`
INSERT INTO edge_routes (hostname, service_name, edge_node, backend_hostname, backend_port, created_at)
VALUES (?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(hostname) DO UPDATE SET
service_name = excluded.service_name,
edge_node = excluded.edge_node,
backend_hostname = excluded.backend_hostname,
backend_port = excluded.backend_port
`, hostname, serviceName, edgeNode, backendHostname, backendPort)
if err != nil {
return fmt.Errorf("create edge route %s: %w", hostname, err)
}
return nil
}
// ListEdgeRoutes returns all edge routes.
func ListEdgeRoutes(db *sql.DB) ([]*EdgeRoute, error) {
return queryEdgeRoutes(db, `SELECT hostname, service_name, edge_node, backend_hostname, backend_port, created_at FROM edge_routes ORDER BY hostname`)
}
// ListEdgeRoutesForService returns edge routes for a specific service.
func ListEdgeRoutesForService(db *sql.DB, serviceName string) ([]*EdgeRoute, error) {
rows, err := db.Query(`
SELECT hostname, service_name, edge_node, backend_hostname, backend_port, created_at
FROM edge_routes WHERE service_name = ? ORDER BY hostname
`, serviceName)
if err != nil {
return nil, fmt.Errorf("list edge routes for %s: %w", serviceName, err)
}
defer func() { _ = rows.Close() }()
return scanEdgeRoutes(rows)
}
// DeleteEdgeRoute removes a single edge route by hostname.
func DeleteEdgeRoute(db *sql.DB, hostname string) error {
_, err := db.Exec(`DELETE FROM edge_routes WHERE hostname = ?`, hostname)
if err != nil {
return fmt.Errorf("delete edge route %s: %w", hostname, err)
}
return nil
}
// DeleteEdgeRoutesForService removes all edge routes for a service.
func DeleteEdgeRoutesForService(db *sql.DB, serviceName string) error {
_, err := db.Exec(`DELETE FROM edge_routes WHERE service_name = ?`, serviceName)
if err != nil {
return fmt.Errorf("delete edge routes for %s: %w", serviceName, err)
}
return nil
}
func queryEdgeRoutes(db *sql.DB, query string) ([]*EdgeRoute, error) {
rows, err := db.Query(query)
if err != nil {
return nil, fmt.Errorf("query edge routes: %w", err)
}
defer func() { _ = rows.Close() }()
return scanEdgeRoutes(rows)
}
func scanEdgeRoutes(rows *sql.Rows) ([]*EdgeRoute, error) {
var routes []*EdgeRoute
for rows.Next() {
var r EdgeRoute
var createdAt string
if err := rows.Scan(&r.Hostname, &r.ServiceName, &r.EdgeNode, &r.BackendHostname, &r.BackendPort, &createdAt); err != nil {
return nil, fmt.Errorf("scan edge route: %w", err)
}
r.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
routes = append(routes, &r)
}
return routes, rows.Err()
}