Add multi hostname per metric support

Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
This commit is contained in:
Lucas Thiesen
2023-05-03 18:13:02 +02:00
parent 65dd585813
commit 02ec2282ab
2 changed files with 55 additions and 19 deletions

View File

@ -2,6 +2,8 @@ package collector
import ( import (
"fmt" "fmt"
"regexp"
"strings"
"time" "time"
autoscalingv2 "k8s.io/api/autoscaling/v2beta2" autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
@ -49,14 +51,20 @@ 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
hostname := config.Config["hostname"] hostnames := config.Config["hostname"]
if hostname == "" { if ok, err := regexp.MatchString("^[a-zA-Z0-9.,-]+$", hostnames); !ok || err != nil {
return nil, fmt.Errorf("hostname not specified, unable to create collector") return nil, fmt.Errorf(
"hostname is not specified or invalid format, unable to create collector",
)
} }
confCopy.Config = map[string]string{ confCopy.Config = map[string]string{
"query": fmt.Sprintf(HostnameRPSQuery, p.metricName, hostname), "query": fmt.Sprintf(
HostnameRPSQuery,
p.metricName,
strings.Replace(strings.Replace(hostnames, ",", "|", -1), ".", "_", -1),
),
} }
c, err := p.promPlugin.NewCollector(hpa, &confCopy, interval) c, err := p.promPlugin.NewCollector(hpa, &confCopy, interval)

View File

@ -47,17 +47,45 @@ func TestHostnamePluginNewCollector(tt *testing.T) {
promPlugin: fakePlugin, promPlugin: fakePlugin,
} }
interval := time.Duration(42) interval := time.Duration(42)
expectedQuery := `scalar(sum(rate(a_valid_one{host=~"foo.bar.baz"}[1m])))`
for _, testcase := range []struct { for _, testcase := range []struct {
msg string msg string
config *MetricConfig config *MetricConfig
shouldWork bool expectedQuery string
shouldWork bool
}{ }{
{"No hostname config", &MetricConfig{Config: make(map[string]string)}, false}, {
{"Nil metric config", nil, false}, "No hostname config",
{"Valid hostname no prom query config", &MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz"}}, true}, &MetricConfig{Config: make(map[string]string)},
{"Valid hostname with prom query config", &MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz", "query": "some_other_query"}}, true}, "",
false,
},
{
"Nil metric config",
nil,
"",
false,
},
{
"Valid hostname no prom query config",
&MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz"}},
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])))`,
true,
},
{
"Multiple valid hostnames no prom query config",
&MetricConfig{Config: map[string]string{"hostname": "foo.bar.baz,foz.bax.bas"}},
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz|foz_bax_bas"}[1m])))`,
true,
},
{
"Valid hostname with prom query config",
&MetricConfig{
Config: map[string]string{"hostname": "foo.bar.baz", "query": "some_other_query"},
},
`scalar(sum(rate(a_valid_one{host=~"foo_bar_baz"}[1m])))`,
true,
},
} { } {
tt.Run(testcase.msg, func(t *testing.T) { tt.Run(testcase.msg, func(t *testing.T) {
c, err := plugin.NewCollector( c, err := plugin.NewCollector(
@ -69,7 +97,7 @@ func TestHostnamePluginNewCollector(tt *testing.T) {
if testcase.shouldWork { if testcase.shouldWork {
require.NotNil(t, c) require.NotNil(t, c)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, fakePlugin.config["query"], expectedQuery) require.Equal(t, testcase.expectedQuery, fakePlugin.config["query"])
} else { } else {
require.Nil(t, c) require.Nil(t, c)
require.NotNil(t, err) require.NotNil(t, err)
@ -123,7 +151,7 @@ func TestHostnameCollectorGetMetrics(tt *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, m) require.NotNil(t, m)
require.Len(t, m, 1) require.Len(t, m, 1)
require.Equal(t, m[0].External.Value, expectedMetric) require.Equal(t, expectedMetric, m[0].External.Value)
} else { } else {
require.NotNil(t, err) require.NotNil(t, err)
require.Nil(t, m) require.Nil(t, m)
@ -151,7 +179,7 @@ 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])))`
hpa := &autoscalingv2.HorizontalPodAutoscaler{ hpa := &autoscalingv2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{ Annotations: map[string]string{
@ -189,12 +217,12 @@ func TestHostnameCollectorAndCollectorFabricInteraction(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
_, ok := c.(*HostnameCollector) _, ok := c.(*HostnameCollector)
require.True(t, ok) require.True(t, ok)
require.Equal(t, fakePlugin.config["query"], expectedQuery) require.Equal(t, expectedQuery, fakePlugin.config["query"])
} }
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])))`
promQuery := "sum(rate(rps[1m]))" promQuery := "sum(rate(rps[1m]))"
hpa := &autoscalingv2.HorizontalPodAutoscaler{ hpa := &autoscalingv2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -256,6 +284,6 @@ func TestHostnamePrometheusCollectorInteraction(t *testing.T) {
hostnameProm, ok := hostname.promCollector.(*PrometheusCollector) hostnameProm, ok := hostname.promCollector.(*PrometheusCollector)
require.True(t, ok) require.True(t, ok)
require.Equal(t, prom.query, promQuery) require.Equal(t, promQuery, prom.query)
require.Equal(t, hostnameProm.query, hostnameQuery) require.Equal(t, hostnameQuery, hostnameProm.query)
} }