From 0e9ade27f3179237374f90129ce5c653b3109fdd Mon Sep 17 00:00:00 2001 From: Lucas Thiesen Date: Tue, 25 Apr 2023 20:05:47 +0200 Subject: [PATCH] [WIP] Add hostname RPS metric collector Signed-off-by: Lucas Thiesen --- pkg/collector/hostname_collector.go | 93 ++++++++++++++++++++++++ pkg/collector/hostname_collector_test.go | 3 + pkg/server/start.go | 17 +++++ 3 files changed, 113 insertions(+) create mode 100644 pkg/collector/hostname_collector.go create mode 100644 pkg/collector/hostname_collector_test.go diff --git a/pkg/collector/hostname_collector.go b/pkg/collector/hostname_collector.go new file mode 100644 index 00000000..1948a5ee --- /dev/null +++ b/pkg/collector/hostname_collector.go @@ -0,0 +1,93 @@ +package collector + +import ( + "fmt" + "time" + + autoscalingv2 "k8s.io/api/autoscaling/v2beta2" +) + +const ( + HostnameMetricType = "hostname" + HostnameRPSQuery = `scalar(sum(rate(%s{host=~"%s"}[1m])))` +) + +type HostnameCollectorPlugin struct { + metricName string + promPlugin CollectorPlugin +} + +type HostnameCollector struct { + interval time.Duration + promCollector Collector +} + +func NewHostnameCollectorPlugin( + promPlugin CollectorPlugin, + metricName string, +) (*HostnameCollectorPlugin, error) { + if metricName == "" { + return nil, fmt.Errorf("failed to initialize hostname collector plugin, metric name was not defined") + } + + return &HostnameCollectorPlugin{ + metricName: metricName, + promPlugin: promPlugin, + }, nil +} + +func (p *HostnameCollectorPlugin) NewCollector( + hpa *autoscalingv2.HorizontalPodAutoscaler, + config *MetricConfig, + interval time.Duration, +) (Collector, error) { + // Need to copy config and add a promQL query in order to get + // RPS data from a specific hostname from prometheus. The idea + // of the copy is to not modify the original config struct. + confCopy := *config + hostname := config.Config["hostname"] + + if hostname == "" { + return nil, fmt.Errorf("hostname not specified, unable to create collector") + } + + confCopy.Config = map[string]string{ + "query": fmt.Sprintf(HostnameRPSQuery, p.metricName, hostname), + } + + c, err := p.promPlugin.NewCollector(hpa, &confCopy, interval) + if err != nil { + return nil, err + } + + return &HostnameCollector{ + interval: interval, + promCollector: c, + }, nil +} + +// GetMetrics gets hostname metrics from Prometheus +func (c *HostnameCollector) GetMetrics() ([]CollectedMetric, error) { + v, err := c.promCollector.GetMetrics() + if err != nil { + return nil, err + } + + if len(v) != 1 { + return nil, fmt.Errorf("expected to only get one metric value, got %d", len(v)) + } + + // TBD(Lucas): + // The explanation bellow is only true if we want to implement object metrics. + // I believe external metrics would suffice. + // Apparently in case of k8s