package feta import "sync" import "time" import "fmt" import "runtime" //import "github.com/gin-gonic/gin" import "github.com/rs/zerolog/log" type InstanceBackend interface { //FIXME } type InstanceManager struct { mu sync.Mutex instances map[InstanceHostname]*Instance newInstanceNotifications chan InstanceHostname startup time.Time addLock sync.Mutex } func NewInstanceManager() *InstanceManager { i := new(InstanceManager) i.instances = make(map[InstanceHostname]*Instance) return i } func (self *InstanceManager) logCaller(msg string) { fpcs := make([]uintptr, 1) // Skip 2 levels to get the caller n := runtime.Callers(3, fpcs) if n == 0 { log.Debug().Msg("MSG: NO CALLER") } caller := runtime.FuncForPC(fpcs[0] - 1) if caller == nil { log.Debug().Msg("MSG CALLER WAS NIL") } // Print the file name and line number filename, line := caller.FileLine(fpcs[0] - 1) function := caller.Name() log.Debug(). Str("filename", filename). Int("linenum", line). Str("function", function). Msg(msg) } func (self *InstanceManager) Lock() { //self.logCaller("instancemanager attempting to lock") self.mu.Lock() //self.logCaller("instancemanager locked") } func (self *InstanceManager) Unlock() { self.mu.Unlock() //self.logCaller("instancemanager unlocked") } func (self *InstanceManager) AddInstanceNotificationChannel(via chan InstanceHostname) { self.Lock() defer self.Unlock() self.newInstanceNotifications = via } func (self *InstanceManager) Manage() { log.Info().Msg("InstanceManager starting") go func() { self.receiveNewInstanceHostnames() }() self.startup = time.Now() for { log.Info().Msg("InstanceManager tick") self.managerLoop() time.Sleep(1 * time.Second) } } func (self *InstanceManager) managerLoop() { self.Lock() il := make([]*Instance, 0) for _, v := range self.instances { il = append(il, v) } self.Unlock() for _, v := range il { if v.dueForFetch() { go func(i *Instance) { i.Fetch() }(v) } } } func (self *InstanceManager) hostnameExists(newhn InstanceHostname) bool { self.Lock() defer self.Unlock() for k, _ := range self.instances { if newhn == k { return true } } return false } func (self *InstanceManager) addInstanceByHostname(newhn InstanceHostname) { // we do these one at a time self.addLock.Lock() defer self.addLock.Unlock() if self.hostnameExists(newhn) { return } i := NewInstance(newhn) // we do node detection under the addLock to avoid thundering // on startup i.detectNodeTypeIfNecessary() self.Lock() defer self.Unlock() self.instances[newhn] = i } func (self *InstanceManager) receiveNewInstanceHostnames() { var newhn InstanceHostname for { newhn = <-self.newInstanceNotifications // receive them fast out of the channel, let the adding function lock to add // them one at a time go func() { self.addInstanceByHostname(newhn) }() } } func (self *InstanceManager) logInstanceReport() { r := self.instanceSummaryReport() log.Info(). Uint("up", r.up). Uint("total", r.total). Uint("identified", r.identified). Msg("instance report") } type InstanceSummaryReport struct { up uint identified uint total uint } func (r *InstanceSummaryReport) String() string { return fmt.Sprintf("up=%d identified=%d total=%d", r.up, r.identified, r.total) } func (self *InstanceManager) NumInstances() uint { return self.instanceSummaryReport().total } type InstanceListReport []*InstanceDetail type InstanceDetail struct { hostname string up bool nextFetch string } func (self *InstanceManager) listInstances() []*Instance { var out []*Instance self.Lock() defer self.Unlock() for _, v := range self.instances { out = append(out, v) } return out } func (self *InstanceManager) instanceSummaryReport() *InstanceSummaryReport { self.Lock() defer self.Unlock() r := new(InstanceSummaryReport) r.total = uint(len(self.instances)) for _, elem := range self.instances { if elem.identified == true { r.identified = r.identified + 1 } if elem.status == InstanceStatusAlive { r.up = r.up + 1 } } return r }