71 lines
2.1 KiB
Go
71 lines
2.1 KiB
Go
package hub
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/shcizo/package-updater/internal/master/model"
|
|
"github.com/shcizo/package-updater/internal/protocol"
|
|
)
|
|
|
|
// candidate is a connection that has finished its hello and may get jobs.
|
|
type candidate struct {
|
|
agentID int64
|
|
connID uint64
|
|
hello protocol.Hello
|
|
}
|
|
|
|
// Route picks the connection a job for svc goes to, among agents that speak
|
|
// at least protocol version minProtocol. It returns model.ErrAgentOffline or
|
|
// model.ErrAgentTooOld when there is none.
|
|
func (h *Hub) Route(svc model.Service, minProtocol int) (model.Route, error) {
|
|
h.mu.Lock()
|
|
conns := make([]candidate, 0, len(h.conns))
|
|
for id, c := range h.conns {
|
|
if c.hello != nil {
|
|
conns = append(conns, candidate{agentID: id, connID: c.id, hello: *c.hello})
|
|
}
|
|
}
|
|
h.mu.Unlock()
|
|
return route(svc, conns, minProtocol)
|
|
}
|
|
|
|
// route picks the connection a job for svc goes to. Compose services and
|
|
// containers can only run on the agent that reported them. Swarm services
|
|
// run on the reporting agent if it qualifies, else on any other connected
|
|
// manager of the same cluster (lowest agent ID first). Too-old wins over
|
|
// offline only when a connection would otherwise have qualified.
|
|
func route(svc model.Service, conns []candidate, minProtocol int) (model.Route, error) {
|
|
sort.Slice(conns, func(a, b int) bool { return conns[a].agentID < conns[b].agentID })
|
|
tooOld := false
|
|
try := func(c candidate) bool {
|
|
if svc.Kind == protocol.KindSwarm {
|
|
if !c.hello.SwarmManager || c.hello.SwarmClusterID == "" || c.hello.SwarmClusterID != svc.SwarmClusterID {
|
|
return false
|
|
}
|
|
}
|
|
if c.hello.ProtocolVersion < minProtocol {
|
|
tooOld = true
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
if svc.AgentID != nil {
|
|
for _, c := range conns {
|
|
if c.agentID == *svc.AgentID && try(c) {
|
|
return model.Route{AgentID: c.agentID, ConnID: c.connID}, nil
|
|
}
|
|
}
|
|
}
|
|
if svc.Kind == protocol.KindSwarm {
|
|
for _, c := range conns {
|
|
if try(c) {
|
|
return model.Route{AgentID: c.agentID, ConnID: c.connID}, nil
|
|
}
|
|
}
|
|
}
|
|
if tooOld {
|
|
return model.Route{}, model.ErrAgentTooOld
|
|
}
|
|
return model.Route{}, model.ErrAgentOffline
|
|
}
|