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>
96 lines
3.0 KiB
Go
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()
|
|
}
|