Add support for traffic weight specification

Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
This commit is contained in:
Lucas Thiesen
2023-05-03 20:49:14 +02:00
parent 02ec2282ab
commit 153d754353
2 changed files with 46 additions and 19 deletions

View File

@ -3,6 +3,7 @@ package collector
import ( import (
"fmt" "fmt"
"regexp" "regexp"
"strconv"
"strings" "strings"
"time" "time"
@ -11,7 +12,7 @@ import (
const ( const (
HostnameMetricType = "hostname-rps" HostnameMetricType = "hostname-rps"
HostnameRPSQuery = `scalar(sum(rate(%s{host=~"%s"}[1m])))` HostnameRPSQuery = `scalar(sum(rate(%s{host=~"%s"}[1m])) * %.4f)`
) )
type HostnameCollectorPlugin struct { type HostnameCollectorPlugin struct {
@ -51,19 +52,37 @@ func (p *HostnameCollectorPlugin) NewCollector(
// RPS data from a specific hostname from prometheus. The idea // RPS data from a specific hostname from prometheus. The idea
// of the copy is to not modify the original config struct. // of the copy is to not modify the original config struct.
confCopy := *config confCopy := *config
hostnames := config.Config["hostname"] //hostnames := config.Config["hostnames"]
//weights := config.Config["weights"]
if ok, err := regexp.MatchString("^[a-zA-Z0-9.,-]+$", hostnames); !ok || err != nil { if _, ok := config.Config["hostnames"]; !ok {
return nil, fmt.Errorf( return nil, fmt.Errorf("hostname is not specified, unable to create collector")
"hostname is not specified or invalid format, unable to create collector", }
) hostnames := strings.Split(config.Config["hostnames"], ",")
for _, h := range hostnames {
if ok, err := regexp.MatchString("^[a-zA-Z0-9.-]+$", h); !ok || err != nil {
return nil, fmt.Errorf(
"Invalid hostname format, unable to create collector: %s",
h,
)
}
}
weight := 1.0
if w, ok := config.Config["weight"]; ok {
num, err := strconv.ParseFloat(w, 64)
if err != nil {
return nil, fmt.Errorf("Could not parse weight annotation, unable to create collector: %s", w)
}
weight = num / 100.0
} }
confCopy.Config = map[string]string{ confCopy.Config = map[string]string{
"query": fmt.Sprintf( "query": fmt.Sprintf(
HostnameRPSQuery, HostnameRPSQuery,
p.metricName, p.metricName,
strings.Replace(strings.Replace(hostnames, ",", "|", -1), ".", "_", -1), strings.Replace(strings.Join(hostnames, "|"), ".", "_", -1),
weight,
), ),
} }

View File

@ -68,22 +68,28 @@ func TestHostnamePluginNewCollector(tt *testing.T) {
}, },
{ {
"Valid hostname no prom query config", "Valid hostname no prom query config",
&MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz"}}, &MetricConfig{Config: map[string]string{"hostnames": "foo.bar.baz"}},
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])))`, `scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])) * 1.0000)`,
true,
},
{
"Valid hostname no prom query config",
&MetricConfig{Config: map[string]string{"hostnames": "foo.bar.baz", "weight": "42"}},
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])) * 0.4200)`,
true, true,
}, },
{ {
"Multiple valid hostnames no prom query config", "Multiple valid hostnames no prom query config",
&MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz,foz.bax.bas"}}, &MetricConfig{Config: map[string]string{"hostnames": "foo.bar.baz,foz.bax.bas"}},
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz|foz_bax_bas"}[1m])))`, `scalar(sum(rate(a_valid_one{host=~"foo_bar_baz|foz_bax_bas"}[1m])) * 1.0000)`,
true, true,
}, },
{ {
"Valid hostname with prom query config", "Valid hostname with prom query config",
&MetricConfig{ &MetricConfig{
Config: map[string]string{"hostname": "foo.bar.baz", "query": "some_other_query"}, Config: map[string]string{"hostnames": "foo.bar.baz", "query": "some_other_query"},
}, },
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])))`, `scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])) * 1.0000)`,
true, true,
}, },
} { } {
@ -169,7 +175,7 @@ func TestHostnameCollectorInterval(t *testing.T) {
} }
c, err := plugin.NewCollector( c, err := plugin.NewCollector(
&autoscalingv2.HorizontalPodAutoscaler{}, &autoscalingv2.HorizontalPodAutoscaler{},
&MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz"}}, &MetricConfig{Config: map[string]string{"hostnames": "foo.bar.baz"}},
interval, interval,
) )
@ -179,11 +185,12 @@ func TestHostnameCollectorInterval(t *testing.T) {
} }
func TestHostnameCollectorAndCollectorFabricInteraction(t *testing.T) { func TestHostnameCollectorAndCollectorFabricInteraction(t *testing.T) {
expectedQuery := `scalar(sum(rate(a_metric{host=~"just_testing_com"}[1m])))` expectedQuery := `scalar(sum(rate(a_metric{host=~"just_testing_com"}[1m])) * 0.4200)`
hpa := &autoscalingv2.HorizontalPodAutoscaler{ hpa := &autoscalingv2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{ Annotations: map[string]string{
"metric-config.external.foo.hostname-rps/hostname": "just.testing.com", "metric-config.external.foo.hostname-rps/hostnames": "just.testing.com",
"metric-config.external.foo.hostname-rps/weight": "42",
}, },
}, },
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
@ -222,13 +229,14 @@ func TestHostnameCollectorAndCollectorFabricInteraction(t *testing.T) {
} }
func TestHostnamePrometheusCollectorInteraction(t *testing.T) { func TestHostnamePrometheusCollectorInteraction(t *testing.T) {
hostnameQuery := `scalar(sum(rate(a_metric{host=~"just_testing_com"}[1m])))` hostnameQuery := `scalar(sum(rate(a_metric{host=~"just_testing_com"}[1m])) * 0.4200)`
promQuery := "sum(rate(rps[1m]))" promQuery := "sum(rate(rps[1m]))"
hpa := &autoscalingv2.HorizontalPodAutoscaler{ hpa := &autoscalingv2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{ Annotations: map[string]string{
"metric-config.external.foo.hostname-rps/hostname": "just.testing.com", "metric-config.external.foo.hostname-rps/hostnames": "just.testing.com",
"metric-config.external.bar.prometheus/query": promQuery, "metric-config.external.foo.hostname-rps/weight": "42",
"metric-config.external.bar.prometheus/query": promQuery,
}, },
}, },
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ Spec: autoscalingv2.HorizontalPodAutoscalerSpec{