feat(backend): implement service monitor proto & connect it to http /agents
ci-agent / build (push) Has been cancelled
ci-agent / build (push) Has been cancelled
This commit is contained in:
@@ -186,3 +186,45 @@ func (c *Collector) Agents() []*Agent {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ServicesStream handles the ServicesUpdate client-streaming RPC.
|
||||
// Agents send service status updates which are stored in the collector.
|
||||
// Returns a single response when the agent closes the stream.
|
||||
func (c *Collector) ServicesStream(stream proto.Collector_ServicesStreamServer) error {
|
||||
md, ok := metadata.FromIncomingContext(stream.Context())
|
||||
if !ok {
|
||||
return fmt.Errorf("no metadata in context")
|
||||
}
|
||||
|
||||
whoamiVals := md["whoami"]
|
||||
if len(whoamiVals) == 0 {
|
||||
return fmt.Errorf("whoami metadata missing")
|
||||
}
|
||||
agentName := whoamiVals[0]
|
||||
|
||||
log.Printf("Agent %s started services update stream", agentName)
|
||||
|
||||
for {
|
||||
update, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
log.Printf("Agent %s finished services update stream", agentName)
|
||||
return stream.SendAndClose(&proto.ServicesUpdateResp{})
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to receive services update: %w", err)
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
if agent, ok := c.agents[agentName]; ok {
|
||||
services := make([]string, 0, len(update.Services))
|
||||
for _, s := range update.Services {
|
||||
services = append(services, fmt.Sprintf("%s:%s", s.Name, s.Status))
|
||||
}
|
||||
agent.Services = services
|
||||
log.Printf("Updated services for agent %s: %v", agentName, agent.Services)
|
||||
} else {
|
||||
log.Printf("Warning: received services update for unknown agent %s", agentName)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,19 @@ func NewAgentsGroup(h *Handlers, coll *collector.Collector) AgentsGroup {
|
||||
return AgentsGroup{Handlers: h, collector: coll}
|
||||
}
|
||||
|
||||
// AgentInfo represents a connected agent's current status.
|
||||
type AgentInfo struct {
|
||||
Token string `json:"token"`
|
||||
Label string `json:"label"`
|
||||
Services []string `json:"services"`
|
||||
ConnectedAt string `json:"connected_at"`
|
||||
Token string `json:"token" example:"agent-001"` // Unique agent identifier
|
||||
Label string `json:"label" example:"web-server-1"` // Human-readable label
|
||||
Services []string `json:"services" example:"nginx:running,redis:up"` // List of services with status (format: "name:status")
|
||||
ConnectedAt string `json:"connected_at" example:"2026-04-04 10:30:00"` // Time when agent connected (RFC3339-like)
|
||||
}
|
||||
|
||||
// @Summary Get connected agents
|
||||
// @Description Returns a list of all agents currently connected via Collector (log streaming)
|
||||
// @Tags agents
|
||||
// @Security Bearer
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {array} AgentInfo
|
||||
// @Router /agents [get]
|
||||
|
||||
@@ -82,7 +82,7 @@ func (self *JobsHandlers) AddJob(c *gin.Context) {
|
||||
return err
|
||||
}
|
||||
job, err := agent.WaitJob(jid)
|
||||
if err != nil && !errors.Is(err, &exec.ExitError{}) {
|
||||
if err != nil && !errors.As(err, &exec.ExitError{}) {
|
||||
return err
|
||||
}
|
||||
c.JSON(http.StatusCreated, AddJobOut{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// TOOD: fuck
|
||||
func RandomToken() (string, error) {
|
||||
token := make([]byte, 32)
|
||||
if _, err := rand.Read(token); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user