package master import ( "context" "fmt" mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1" "git.wntrmute.dev/mc/mcp/internal/masterdb" ) // Status returns the status of services across the fleet. func (m *Master) Status(ctx context.Context, req *mcpv1.MasterStatusRequest) (*mcpv1.MasterStatusResponse, error) { m.Logger.Debug("Status", "service", req.GetServiceName()) resp := &mcpv1.MasterStatusResponse{} // If a specific service is requested, look up its placement. if name := req.GetServiceName(); name != "" { placement, err := masterdb.GetPlacement(m.DB, name) if err != nil { return nil, fmt.Errorf("lookup placement: %w", err) } if placement == nil { return resp, nil // empty — service not found } ss := m.getServiceStatus(ctx, placement) resp.Services = append(resp.Services, ss) return resp, nil } // All services. placements, err := masterdb.ListPlacements(m.DB) if err != nil { return nil, fmt.Errorf("list placements: %w", err) } for _, p := range placements { ss := m.getServiceStatus(ctx, p) resp.Services = append(resp.Services, ss) } return resp, nil } func (m *Master) getServiceStatus(ctx context.Context, p *masterdb.Placement) *mcpv1.ServiceStatus { ss := &mcpv1.ServiceStatus{ Name: p.ServiceName, Node: p.Node, Tier: p.Tier, Status: "unknown", } // Query the agent for live status. client, err := m.Pool.Get(p.Node) if err != nil { ss.Status = "unreachable" return ss } statusCtx, cancel := context.WithTimeout(ctx, m.Config.Timeouts.HealthCheck.Duration) defer cancel() agentResp, err := client.GetServiceStatus(statusCtx, &mcpv1.GetServiceStatusRequest{ Name: p.ServiceName, }) if err != nil { ss.Status = "unreachable" return ss } // Map agent status to master status. for _, info := range agentResp.GetServices() { if info.GetName() == p.ServiceName { if info.GetActive() { ss.Status = "running" } else { ss.Status = "stopped" } break } } // Attach edge route info. edgeRoutes, err := masterdb.ListEdgeRoutesForService(m.DB, p.ServiceName) if err == nil { for _, er := range edgeRoutes { ss.EdgeRoutes = append(ss.EdgeRoutes, &mcpv1.EdgeRouteStatus{ Hostname: er.Hostname, EdgeNode: er.EdgeNode, }) } } return ss } // ListNodes returns all nodes in the registry with placement counts. func (m *Master) ListNodes(_ context.Context, _ *mcpv1.ListNodesRequest) (*mcpv1.ListNodesResponse, error) { m.Logger.Debug("ListNodes") nodes, err := masterdb.ListNodes(m.DB) if err != nil { return nil, fmt.Errorf("list nodes: %w", err) } counts, err := masterdb.CountPlacementsPerNode(m.DB) if err != nil { return nil, fmt.Errorf("count placements: %w", err) } resp := &mcpv1.ListNodesResponse{} for _, n := range nodes { ni := &mcpv1.NodeInfo{ Name: n.Name, Role: n.Role, Address: n.Address, Arch: n.Arch, Status: n.Status, Containers: int32(n.Containers), //nolint:gosec // small number Services: int32(counts[n.Name]), //nolint:gosec // small number } if n.LastHeartbeat != nil { ni.LastHeartbeat = n.LastHeartbeat.Format("2006-01-02T15:04:05Z") } resp.Nodes = append(resp.Nodes, ni) } return resp, nil }