157 lines
4.0 KiB
Go
157 lines
4.0 KiB
Go
package routing
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"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"
|
|
)
|
|
|
|
const (
|
|
// the name of the ConfigMap that stores the routing table
|
|
ConfigMapRoutingTableName = "keda-http-routing-table"
|
|
// the key in the ConfigMap data that stores the JSON routing table
|
|
configMapRoutingTableKey = "routing-table"
|
|
)
|
|
|
|
// SaveTableToConfigMap saves the contents of table to the Data field in
|
|
// configMap
|
|
func SaveTableToConfigMap(table *Table, configMap *corev1.ConfigMap) error {
|
|
tableAsJSON, err := table.MarshalJSON()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
configMap.Data[configMapRoutingTableKey] = string(tableAsJSON)
|
|
return nil
|
|
}
|
|
|
|
// FetchTableFromConfigMap fetches the Data field from configMap, converts it
|
|
// to a routing table, and returns it
|
|
func FetchTableFromConfigMap(configMap *corev1.ConfigMap, q queue.Counter) (*Table, error) {
|
|
data, found := configMap.Data[configMapRoutingTableKey]
|
|
if !found {
|
|
return nil, fmt.Errorf(
|
|
"no '%s' key found in the %s ConfigMap",
|
|
configMapRoutingTableKey,
|
|
ConfigMapRoutingTableName,
|
|
)
|
|
}
|
|
ret := NewTable()
|
|
if err := ret.UnmarshalJSON([]byte(data)); err != nil {
|
|
retErr := errors.Wrap(
|
|
err,
|
|
fmt.Sprintf(
|
|
"error decoding '%s' key in %s ConfigMap",
|
|
configMapRoutingTableKey,
|
|
ConfigMapRoutingTableName,
|
|
),
|
|
)
|
|
return nil, retErr
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
// updateQueueFromTable ensures that every host in the routing table
|
|
// exists in the given queue, and no hosts exist in the queue that
|
|
// don't exist in the routing table. It uses q.Ensure() and q.Remove()
|
|
// to do those things, respectively.
|
|
func updateQueueFromTable(
|
|
lggr logr.Logger,
|
|
table *Table,
|
|
q queue.Counter,
|
|
) error {
|
|
// ensure that every host is in the queue, even if it has
|
|
// zero pending requests. This is important so that the
|
|
// scaler can report on all applications.
|
|
for host := range table.m {
|
|
q.Ensure(host)
|
|
}
|
|
|
|
// ensure that the queue doesn't have any extra hosts that don't exist in the table
|
|
qCur, err := q.Current()
|
|
if err != nil {
|
|
lggr.Error(
|
|
err,
|
|
"failed to get current queue counts (in order to prune it of missing routing table hosts)",
|
|
)
|
|
return errors.Wrap(err, "pkg.routing.updateQueueFromTable")
|
|
}
|
|
for host := range qCur.Counts {
|
|
if _, err := table.Lookup(host); err != nil {
|
|
q.Remove(host)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetTable fetches the contents of the appropriate ConfigMap that stores
|
|
// the routing table, then tries to decode it into a temporary routing table
|
|
// data structure.
|
|
//
|
|
// If that succeeds, it calls table.Replace(newTable), then ensures that
|
|
// every host in the routing table exists in the given queue, and no hosts
|
|
// exist in the queue that don't exist in the routing table. It uses q.Ensure()
|
|
// and q.Remove() to do those things, respectively.
|
|
func GetTable(
|
|
ctx context.Context,
|
|
lggr logr.Logger,
|
|
getter k8s.ConfigMapGetter,
|
|
table *Table,
|
|
q queue.Counter,
|
|
) error {
|
|
lggr = lggr.WithName("pkg.routing.GetTable")
|
|
|
|
cm, err := getter.Get(
|
|
ctx,
|
|
ConfigMapRoutingTableName,
|
|
metav1.GetOptions{},
|
|
)
|
|
if err != nil {
|
|
lggr.Error(
|
|
err,
|
|
"failed to fetch routing table config map",
|
|
"configMapName",
|
|
ConfigMapRoutingTableName,
|
|
)
|
|
return errors.Wrap(
|
|
err,
|
|
fmt.Sprintf(
|
|
"failed to fetch ConfigMap %s",
|
|
ConfigMapRoutingTableName,
|
|
),
|
|
)
|
|
}
|
|
newTable, err := FetchTableFromConfigMap(cm, q)
|
|
if err != nil {
|
|
lggr.Error(
|
|
err,
|
|
"failed decoding routing table ConfigMap",
|
|
"configMapName",
|
|
ConfigMapRoutingTableName,
|
|
)
|
|
return errors.Wrap(
|
|
err,
|
|
fmt.Sprintf(
|
|
"failed decoding ConfigMap %s into a routing table",
|
|
ConfigMapRoutingTableName,
|
|
),
|
|
)
|
|
}
|
|
|
|
table.Replace(newTable)
|
|
if err := updateQueueFromTable(lggr, table, q); err != nil {
|
|
lggr.Error(
|
|
err,
|
|
"unable to update the queue from the new routing table",
|
|
)
|
|
return errors.Wrap(err, "pkg.routing.GetTable")
|
|
}
|
|
|
|
return nil
|
|
}
|