commit 43c71de36cec7d072d2436925b93cf2e94b71606 from: Oliver Lowe date: Thu Dec 01 02:58:55 2022 UTC wip commit - f21c51c745099616c3451e39569df1b46feae6af commit + 43c71de36cec7d072d2436925b93cf2e94b71606 blob - c24b1c8b4b98bd3a7e656f48e9f62c5cc40912a7 blob + 493802b3395fcdbcea77e51d9182872d21248345 --- checker.go +++ checker.go @@ -124,3 +124,21 @@ func scheduleCheck(c *Client, filter checkFilter) erro } return fmt.Errorf("%s", resp.Status) } + +func nextCheck(objects []checker) time.Time { + soonest := time.Time{} + for _, obj := range objects { + next := time.Time{} + switch obj.(type); obj { + case Host, Service: + next = obj.NextCheck + } + if soonest.IsZero() { + soonest = next + } + if next.Before(soonest) { + soonest = next + } + } + return soonest +} blob - /dev/null blob + d619bf13ce259580676700dc4f34061acc56a17d (mode 644) --- /dev/null +++ cmd/i2cache/i2cache.go @@ -0,0 +1,65 @@ +package main + +type Cache struct { + client *icinga.Client + hosts map[string]*hostEntry + services map[string]*serviceEntry +} + +func NewCache(client *icinga.Client) *Cache { + return &Cache{ + client: client, + hosts: make(map[string]*hostEntry), + services: make(map[string]*serviceEntry), + } +} + +type serviceEntry struct { + accessTime time.Time + watcher *ServiceWatcher + services []Service +} + +func (c *Cache) GetServices(expr string) []icinga.Service { + entry, ok := c.services[expr] + if !ok { + return nil + } + entry.accessTime = time.Now() + if entry.wacher == nil { + entry := entry + entry.watcher = WatchServices(c.client, expr) + go func() { + for msg := range entry.watcher.Updates { + if msg.Err != nil { + // TODO(otl) ?? + } + entry.services = msg.Services + } + }() + } + return entry.services +} + +type hostEntry struct { + accessTime time.Time + hosts []Host +} + +func (c *Cache) GetHosts(expr string) *hostEntry {} + +func (c *Cache) evictOlderThan(d time.Duration) { + t := time.Now().Sub(d) + for _, entry := range c.services { + if entry.accessTime.Before(t) { + entry.kill <- true + delete(c.services[expr]) + } + } + for _, entry := range c.Hosts { + if entry.accessTime.Before(t) { + entry.kill <- true + delete(c.hosts[expr]) + } + } +} blob - /dev/null blob + 053c8422dbb89f47a6db1921ef5d82dcc969e749 (mode 644) --- /dev/null +++ watch.go @@ -0,0 +1,71 @@ +package icinga + +// A ServiceWatcher continuously queries Icinga about a set of Services. +// A ServiceWatcher uses the next check time of Services to determine when to query Icinga again. +// If it cannot determine the next check time, the Watcher waits 1 minute before trying again. +type ServiceWatcher struct { + kill chan bool + Updates chan ServiceMsg +} + +type HostWatcher struct { + Kill chan bool + Updates chan HostMsg +} + +type ServiceMsg struct { + Services []Service + Err error +} + +type HostMsg struct { + Hosts []Host + Err error +} + +// Kill stops the Watcher irrevocably. +// A new Watcher must be created with WatchServices. +func (w *ServiceWatcher) Kill() { + w.kill <- true +} + +// WatchServices returns a new Watcher which uses client to +// continuously query Icinga for Services matching the given filter +// expression. +func WatchServices(client *Client, expr string) *Watcher { + timer := time.NewTimer(0) + kill := make(chan bool) + ch := make(chan ServiceMsg) + go func() { + select { + case <-kill: + close(ch) + return + case <-timer.C: + services, err := client.Services(expr) + ch <- ServiceMsg{services, err} + if err != nil { + timer.Reset(1*time.Minute) + } else { + timer.Reset(time.Until(nextCheck(services))) + } + } + } + return &Watcher{ + kill: kill + Updates: ch + } +} + +func WatchHosts(expr string, ch chan HostMsg, stop chan bool) { + timer := time.NewTimer(0) + select { + case <-stop: + close(ch) + return + case <-timer.C: + hosts, err := client.Hosts(expr) + ch <- HostMsg{hosts, err} + timer.Reset(time.Until(NextCheck(hosts))) + } +}