feat(master): route jobs to agents and reconcile them on reconnect (#7)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
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. It returns
|
||||
// model.ErrAgentOffline or model.ErrAgentTooOld when there is none.
|
||||
func (h *Hub) Route(svc model.Service) (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)
|
||||
}
|
||||
|
||||
// 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) (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 < protocol.MinJobVersion {
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user