package main import "sync" import "time" import "fmt" import "runtime" 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 } 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() { self.managerLoop() } func (self *InstanceManager) managerLoop() { log.Info().Msg("InstanceManager starting") go self.receiveNewInstanceHostnames() self.startup = time.Now() for { log.Info().Msg("InstanceManager tick") self.Lock() for _, v := range self.instances { go func() { if v.dueForFetch() { v.Fetch() } }() } self.Unlock() time.Sleep(1 * time.Second) } } func (self *InstanceManager) hostnameExists(newhn InstanceHostname) bool { self.Lock() for k, _ := range self.instances { if newhn == k { self.Unlock() return true } } self.Unlock() return false } func (self *InstanceManager) addInstanceByHostname(newhn InstanceHostname) { // only add it if we haven't seen the hostname before if !self.hostnameExists(newhn) { i := NewInstance(newhn) self.Lock() self.instances[newhn] = i self.Unlock() } } func (self *InstanceManager) receiveNewInstanceHostnames() { var newhn InstanceHostname for { newhn = <-self.newInstanceNotifications self.addInstanceByHostname(newhn) } } func (self *InstanceManager) logInstanceReport() { r := self.instanceReport() log.Info(). Uint("up", r.up). Uint("total", r.total). Uint("identified", r.identified). Msg("instance report") } type InstanceReport struct { up uint identified uint total uint } func (r *InstanceReport) String() string { return fmt.Sprintf("up=%d identified=%d total=%d", r.up, r.identified, r.total) } func (self *InstanceManager) NumInstances() uint { return self.instanceReport().total } type InstanceListReport []*InstanceDetail type InstanceDetail struct { hostname string up bool nextFetch string } func (self *InstanceManager) instanceListForApi() InstanceListReport { var output InstanceListReport self.Lock() defer self.Unlock() for _, v := range self.instances { id := &InstanceDetail{ hostname: v.hostname, } id.up = v.Up() id.nextFetch = string(time.Now().Sub(v.nextFetch)) output = append(output, id) fmt.Printf("%s", output) } return output } func (self *InstanceManager) instanceReport() *InstanceReport { self.Lock() defer self.Unlock() r := new(InstanceReport) 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 }