http-add-on/pkg/routing/config_map_updater.go

89 lines
2.7 KiB
Go

package routing
import (
"context"
"time"
"github.com/go-logr/logr"
"github.com/kedacore/http-add-on/pkg/k8s"
"github.com/kedacore/http-add-on/pkg/queue"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
)
// StartConfigMapRoutingTableUpdater starts a loop that does the following:
//
// - Fetches a full version of the ConfigMap called ConfigMapRoutingTableName in
// the given namespace ns, and calls table.Replace(newTable) after it does so
// - Uses watcher to watch for all ADDED or CREATED events on the ConfigMap
// called ConfigMapRoutingTableName. On either of those events, decodes
// that ConfigMap into a routing table and stores the new table into table
// using table.Replace(newTable)
// - Returns an appropriate non-nil error if ctx.Done() receives
func StartConfigMapRoutingTableUpdater(
ctx context.Context,
lggr logr.Logger,
updateEvery time.Duration,
getterWatcher k8s.ConfigMapGetterWatcher,
table *Table,
q queue.Counter,
) error {
lggr = lggr.WithName("pkg.routing.StartConfigMapRoutingTableUpdater")
watchIface, err := getterWatcher.Watch(ctx, metav1.ListOptions{})
if err != nil {
return err
}
defer watchIface.Stop()
ticker := time.NewTicker(updateEvery)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return errors.Wrap(ctx.Err(), "context is done")
case <-ticker.C:
if err := GetTable(ctx, lggr, getterWatcher, table, q); err != nil {
return errors.Wrap(err, "failed to fetch routing table")
}
case evt := <-watchIface.ResultChan():
evtType := evt.Type
obj := evt.Object
if evtType == watch.Added || evtType == watch.Modified {
cm, ok := obj.(*corev1.ConfigMap)
// by definition of watchIface, all returned objects should
// be assertable to a ConfigMap. In the likely impossible
// case that it isn't, just ignore and move on.
// This check is just to be defensive.
if !ok {
continue
}
// the watcher is open on all ConfigMaps in the namespace, so
// bail out of this loop iteration immediately if the event
// isn't for the routing table ConfigMap.
if cm.Name != ConfigMapRoutingTableName {
continue
}
newTable, err := FetchTableFromConfigMap(cm, q)
if err != nil {
return err
}
table.Replace(newTable)
if err := updateQueueFromTable(lggr, table, q); err != nil {
// if we couldn't update the queue, just log but don't bail.
// we want to give the loop a chance to tick (or receive a new event)
// and update the table & queue again
lggr.Error(
err,
"failed to update queue from table on ConfigMap change event",
)
continue
}
}
}
}
}