package agentsvc import ( "context" "slices" "time" llmgwv1 "gitea.mynodes.xyz/ilker/llmgw/api/gen/llmgw/v1" ) // SetControlPlanes gives the service where the list of control planes comes // from. Every agent is told it when it registers, and again when it changes. func (s *Service) SetControlPlanes(list func(context.Context) ([]string, error)) { s.mu.Lock() s.controlPlanes = list s.mu.Unlock() } // controlPlanesMsg is the list as an agent is sent it, or nil when there is // none to send: a cluster with no list recorded keeps what its agents were // configured with. func (s *Service) controlPlanesMsg(ctx context.Context) *llmgwv1.ControlMessage { s.mu.RLock() list := s.controlPlanes s.mu.RUnlock() if list == nil { return nil } servers, err := list(ctx) if err != nil || len(servers) == 0 { return nil } return &llmgwv1.ControlMessage{Msg: &llmgwv1.ControlMessage_ControlPlanes{ ControlPlanes: &llmgwv1.ControlPlanes{Servers: servers}}} } // WatchControlPlanes tells every connected agent the list when it changes, // checking every so often. A control plane added by `llmgw server add` reaches // the nodes within that time, without their being touched. func (s *Service) WatchControlPlanes(ctx context.Context, every time.Duration) { var last []string t := time.NewTicker(every) defer t.Stop() for { if msg := s.controlPlanesMsg(ctx); msg != nil { now := msg.GetControlPlanes().GetServers() if !slices.Equal(now, last) { for _, node := range s.ConnectedNodes() { if err := s.send(node, msg); err != nil { s.log.Warn("could not tell a node the control planes", "node", node, "err", err) } } if last != nil { s.log.Info("told the nodes the control planes changed", "servers", now) } last = now } } select { case <-ctx.Done(): return case <-t.C: } } }