diff --git a/pkg/collector/aws_collector.go b/pkg/collector/aws_collector.go index 18fab5e3..72101c82 100644 --- a/pkg/collector/aws_collector.go +++ b/pkg/collector/aws_collector.go @@ -33,7 +33,7 @@ func NewAWSCollectorPlugin(sessions map[string]*session.Session) *AWSCollectorPl // NewCollector initializes a new skipper collector from the specified HPA. func (c *AWSCollectorPlugin) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) { - return NewAWSSQSCollector(c.sessions, config, interval) + return NewAWSSQSCollector(c.sessions, hpa, config, interval) } type AWSSQSCollector struct { @@ -42,11 +42,12 @@ type AWSSQSCollector struct { region string queueURL string queueName string + namespace string metric autoscalingv2.MetricIdentifier metricType autoscalingv2.MetricSourceType } -func NewAWSSQSCollector(sessions map[string]*session.Session, config *MetricConfig, interval time.Duration) (*AWSSQSCollector, error) { +func NewAWSSQSCollector(sessions map[string]*session.Session, hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (*AWSSQSCollector, error) { if config.Metric.Selector == nil { return nil, fmt.Errorf("selector for queue is not specified") } @@ -80,6 +81,7 @@ func NewAWSSQSCollector(sessions map[string]*session.Session, config *MetricConf interval: interval, queueURL: aws.StringValue(resp.QueueUrl), queueName: name, + namespace: hpa.Namespace, metric: config.Metric, metricType: config.Type, }, nil @@ -103,7 +105,8 @@ func (c *AWSSQSCollector) GetMetrics() ([]CollectedMetric, error) { } metricValue := CollectedMetric{ - Type: c.metricType, + Namespace: c.namespace, + Type: c.metricType, External: external_metrics.ExternalMetricValue{ MetricName: c.metric.Name, MetricLabels: c.metric.Selector.MatchLabels, diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 85b942bd..e1e2502c 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -177,9 +177,10 @@ type MetricTypeName struct { } type CollectedMetric struct { - Type autoscalingv2.MetricSourceType - Custom custom_metrics.MetricValue - External external_metrics.ExternalMetricValue + Type autoscalingv2.MetricSourceType + Namespace string + Custom custom_metrics.MetricValue + External external_metrics.ExternalMetricValue } type Collector interface { diff --git a/pkg/collector/http_collector.go b/pkg/collector/http_collector.go index 1987c25c..719bb1f8 100644 --- a/pkg/collector/http_collector.go +++ b/pkg/collector/http_collector.go @@ -27,8 +27,10 @@ func NewHTTPCollectorPlugin() (*HTTPCollectorPlugin, error) { return &HTTPCollectorPlugin{}, nil } -func (p *HTTPCollectorPlugin) NewCollector(_ *v2beta2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) { - collector := &HTTPCollector{} +func (p *HTTPCollectorPlugin) NewCollector(hpa *v2beta2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) { + collector := &HTTPCollector{ + namespace: hpa.Namespace, + } var ( value string ok bool @@ -74,6 +76,7 @@ func (p *HTTPCollectorPlugin) NewCollector(_ *v2beta2.HorizontalPodAutoscaler, c type HTTPCollector struct { endpoint *url.URL interval time.Duration + namespace string metricType v2beta2.MetricSourceType metricsGetter *httpmetrics.JSONPathMetricsGetter metric v2beta2.MetricIdentifier @@ -86,7 +89,8 @@ func (c *HTTPCollector) GetMetrics() ([]CollectedMetric, error) { } value := CollectedMetric{ - Type: c.metricType, + Namespace: c.namespace, + Type: c.metricType, External: external_metrics.ExternalMetricValue{ MetricName: c.metric.Name, MetricLabels: c.metric.Selector.MatchLabels, diff --git a/pkg/collector/http_collector_test.go b/pkg/collector/http_collector_test.go index 5e6e0420..9a18eb26 100644 --- a/pkg/collector/http_collector_test.go +++ b/pkg/collector/http_collector_test.go @@ -8,6 +8,8 @@ import ( "github.com/stretchr/testify/require" "k8s.io/api/autoscaling/v2beta2" + autoscalingv2 "k8s.io/api/autoscaling/v2beta2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -60,7 +62,12 @@ func TestHTTPCollector(t *testing.T) { plugin, err := NewHTTPCollectorPlugin() require.NoError(t, err) testConfig := makeTestHTTPCollectorConfig(testServer, tc.aggregator) - collector, err := plugin.NewCollector(nil, testConfig, testInterval) + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + } + collector, err := plugin.NewCollector(hpa, testConfig, testInterval) require.NoError(t, err) metrics, err := collector.GetMetrics() require.NoError(t, err) diff --git a/pkg/collector/influxdb_collector.go b/pkg/collector/influxdb_collector.go index 8acdedc1..4c7ee4b8 100644 --- a/pkg/collector/influxdb_collector.go +++ b/pkg/collector/influxdb_collector.go @@ -40,7 +40,7 @@ func NewInfluxDBCollectorPlugin(client kubernetes.Interface, address, token, org } func (p *InfluxDBCollectorPlugin) NewCollector(hpa *v2beta2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) { - return NewInfluxDBCollector(p.address, p.token, p.org, config, interval) + return NewInfluxDBCollector(hpa, p.address, p.token, p.org, config, interval) } type InfluxDBCollector struct { @@ -53,13 +53,15 @@ type InfluxDBCollector struct { metric autoscalingv2.MetricIdentifier metricType autoscalingv2.MetricSourceType query string + namespace string } -func NewInfluxDBCollector(address string, token string, org string, config *MetricConfig, interval time.Duration) (*InfluxDBCollector, error) { +func NewInfluxDBCollector(hpa *v2beta2.HorizontalPodAutoscaler, address string, token string, org string, config *MetricConfig, interval time.Duration) (*InfluxDBCollector, error) { collector := &InfluxDBCollector{ interval: interval, metric: config.Metric, metricType: config.Type, + namespace: hpa.Namespace, } switch configType := config.Type; configType { case autoscalingv2.ObjectMetricSourceType: @@ -135,7 +137,8 @@ func (c *InfluxDBCollector) GetMetrics() ([]CollectedMetric, error) { return nil, err } cm := CollectedMetric{ - Type: c.metricType, + Namespace: c.namespace, + Type: c.metricType, External: external_metrics.ExternalMetricValue{ MetricName: c.metric.Name, MetricLabels: c.metric.Selector.MatchLabels, diff --git a/pkg/collector/influxdb_collector_test.go b/pkg/collector/influxdb_collector_test.go index cf67d225..c068dfe3 100644 --- a/pkg/collector/influxdb_collector_test.go +++ b/pkg/collector/influxdb_collector_test.go @@ -6,10 +6,17 @@ import ( "time" "k8s.io/api/autoscaling/v2beta2" + autoscalingv2 "k8s.io/api/autoscaling/v2beta2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestInfluxDBCollector_New(t *testing.T) { + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + } t.Run("simple", func(t *testing.T) { m := &MetricConfig{ MetricTypeName: MetricTypeName{ @@ -32,7 +39,7 @@ func TestInfluxDBCollector_New(t *testing.T) { "query-name": "range2m", }, } - c, err := NewInfluxDBCollector("http://localhost:9999", "secret", "deadbeef", m, time.Second) + c, err := NewInfluxDBCollector(hpa, "http://localhost:9999", "secret", "deadbeef", m, time.Second) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -73,7 +80,7 @@ func TestInfluxDBCollector_New(t *testing.T) { "query-name": "range3m", }, } - c, err := NewInfluxDBCollector("http://localhost:8888", "secret", "deadbeef", m, time.Second) + c, err := NewInfluxDBCollector(hpa, "http://localhost:8888", "secret", "deadbeef", m, time.Second) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -143,7 +150,7 @@ func TestInfluxDBCollector_New(t *testing.T) { CollectorType: "influxdb", Config: tc.config, } - _, err := NewInfluxDBCollector("http://localhost:9999", "secret", "deadbeef", m, time.Second) + _, err := NewInfluxDBCollector(hpa, "http://localhost:9999", "secret", "deadbeef", m, time.Second) if err == nil { t.Fatal("expected error got none") } diff --git a/pkg/collector/pod_collector.go b/pkg/collector/pod_collector.go index 975e973f..499c0156 100644 --- a/pkg/collector/pod_collector.go +++ b/pkg/collector/pod_collector.go @@ -118,7 +118,8 @@ func (c *PodCollector) getPodMetric(pod corev1.Pod, ch chan CollectedMetric, err } ch <- CollectedMetric{ - Type: c.metricType, + Namespace: c.namespace, + Type: c.metricType, Custom: custom_metrics.MetricValue{ DescribedObject: custom_metrics.ObjectReference{ APIVersion: "v1", diff --git a/pkg/collector/prometheus_collector.go b/pkg/collector/prometheus_collector.go index 36445422..8a90591d 100644 --- a/pkg/collector/prometheus_collector.go +++ b/pkg/collector/prometheus_collector.go @@ -174,7 +174,8 @@ func (c *PrometheusCollector) GetMetrics() ([]CollectedMetric, error) { switch c.metricType { case autoscalingv2.ObjectMetricSourceType: metricValue = CollectedMetric{ - Type: c.metricType, + Namespace: c.hpa.Namespace, + Type: c.metricType, Custom: custom_metrics.MetricValue{ DescribedObject: c.objectReference, Metric: custom_metrics.MetricIdentifier{Name: c.metric.Name, Selector: c.metric.Selector}, @@ -184,7 +185,8 @@ func (c *PrometheusCollector) GetMetrics() ([]CollectedMetric, error) { } case autoscalingv2.ExternalMetricSourceType: metricValue = CollectedMetric{ - Type: c.metricType, + Namespace: c.hpa.Namespace, + Type: c.metricType, External: external_metrics.ExternalMetricValue{ MetricName: c.metric.Name, MetricLabels: c.metric.Selector.MatchLabels, diff --git a/pkg/collector/zmon_collector.go b/pkg/collector/zmon_collector.go index 1315895e..3197e4fd 100644 --- a/pkg/collector/zmon_collector.go +++ b/pkg/collector/zmon_collector.go @@ -43,7 +43,7 @@ func NewZMONCollectorPlugin(zmon zmon.ZMON) (*ZMONCollectorPlugin, error) { // NewCollector initializes a new ZMON collector from the specified HPA. func (c *ZMONCollectorPlugin) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) { - return NewZMONCollector(c.zmon, config, interval) + return NewZMONCollector(c.zmon, hpa, config, interval) } // ZMONCollector defines a collector that is able to collect metrics from ZMON. @@ -57,10 +57,11 @@ type ZMONCollector struct { aggregators []string metric autoscalingv2.MetricIdentifier metricType autoscalingv2.MetricSourceType + namespace string } // NewZMONCollector initializes a new ZMONCollector. -func NewZMONCollector(zmon zmon.ZMON, config *MetricConfig, interval time.Duration) (*ZMONCollector, error) { +func NewZMONCollector(zmon zmon.ZMON, hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (*ZMONCollector, error) { if config.Metric.Selector == nil { return nil, fmt.Errorf("selector for zmon-check is not specified") } @@ -117,6 +118,7 @@ func NewZMONCollector(zmon zmon.ZMON, config *MetricConfig, interval time.Durati aggregators: aggregators, metric: config.Metric, metricType: config.Type, + namespace: hpa.Namespace, }, nil } @@ -136,7 +138,8 @@ func (c *ZMONCollector) GetMetrics() ([]CollectedMetric, error) { point := dataPoints[len(dataPoints)-1] metricValue := CollectedMetric{ - Type: c.metricType, + Namespace: c.namespace, + Type: c.metricType, External: external_metrics.ExternalMetricValue{ MetricName: c.metric.Name, MetricLabels: c.metric.Selector.MatchLabels, diff --git a/pkg/collector/zmon_collector_test.go b/pkg/collector/zmon_collector_test.go index 2b266e19..7339b887 100644 --- a/pkg/collector/zmon_collector_test.go +++ b/pkg/collector/zmon_collector_test.go @@ -96,7 +96,8 @@ func TestZMONCollectorGetMetrics(tt *testing.T) { }, collectedMetrics: []CollectedMetric{ { - Type: config.Type, + Namespace: "default", + Type: config.Type, External: external_metrics.ExternalMetricValue{ MetricName: config.Metric.Name, MetricLabels: config.Metric.Selector.MatchLabels, @@ -115,7 +116,13 @@ func TestZMONCollectorGetMetrics(tt *testing.T) { dataPoints: ti.dataPoints, } - zmonCollector, err := NewZMONCollector(z, config, 1*time.Second) + hpa := &autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + }, + } + + zmonCollector, err := NewZMONCollector(z, hpa, config, 1*time.Second) require.NoError(t, err) metrics, _ := zmonCollector.GetMetrics() diff --git a/pkg/provider/hpa.go b/pkg/provider/hpa.go index 6e4d31f5..ad6356ae 100644 --- a/pkg/provider/hpa.go +++ b/pkg/provider/hpa.go @@ -129,6 +129,7 @@ func (p *HPAProvider) updateHPAs() error { newHPAs := 0 for _, hpa := range hpas.Items { + hpa := *hpa.DeepCopy() resourceRef := resourceReference{ Name: hpa.Name, Namespace: hpa.Namespace, @@ -246,7 +247,8 @@ func (p *HPAProvider) collectMetrics(ctx context.Context) { value.Custom.DescribedObject.Name, ) case autoscalingv2.ExternalMetricSourceType: - p.logger.Infof("Collected new external metric '%s' (%s) [%s]", + p.logger.Infof("Collected new external metric '%s/%s' (%s) [%s]", + value.Namespace, value.External.MetricName, value.External.Value.String(), labels.Set(value.External.MetricLabels).String(), diff --git a/pkg/provider/metric_store.go b/pkg/provider/metric_store.go index 0b013daf..eaeaa9de 100644 --- a/pkg/provider/metric_store.go +++ b/pkg/provider/metric_store.go @@ -31,8 +31,10 @@ type externalMetricsStoredMetric struct { // MetricStore is a simple in-memory Metrics Store for HPA metrics. type MetricStore struct { - customMetricsStore map[string]map[schema.GroupResource]map[string]map[string]customMetricsStoredMetric - externalMetricsStore map[string]map[string]externalMetricsStoredMetric + // metricName -> referencedResource -> objectNamespace -> objectName -> metric + customMetricsStore map[string]map[schema.GroupResource]map[string]map[string]customMetricsStoredMetric + // namespace -> metricName -> labels -> metric + externalMetricsStore map[string]map[string]map[string]externalMetricsStoredMetric metricsTTLCalculator func() time.Time sync.RWMutex } @@ -41,7 +43,7 @@ type MetricStore struct { func NewMetricStore(ttlCalculator func() time.Time) *MetricStore { return &MetricStore{ customMetricsStore: make(map[string]map[schema.GroupResource]map[string]map[string]customMetricsStoredMetric, 0), - externalMetricsStore: make(map[string]map[string]externalMetricsStoredMetric, 0), + externalMetricsStore: make(map[string]map[string]map[string]externalMetricsStoredMetric, 0), metricsTTLCalculator: ttlCalculator, } } @@ -52,7 +54,7 @@ func (s *MetricStore) Insert(value collector.CollectedMetric) { case autoscalingv2.ObjectMetricSourceType, autoscalingv2.PodsMetricSourceType: s.insertCustomMetric(value.Custom) case autoscalingv2.ExternalMetricSourceType: - s.insertExternalMetric(value.External) + s.insertExternalMetric(value.Namespace, value.External) } } @@ -120,7 +122,7 @@ func (s *MetricStore) insertCustomMetric(value custom_metrics.MetricValue) { } // insertExternalMetric inserts an external metric into the store. -func (s *MetricStore) insertExternalMetric(metric external_metrics.ExternalMetricValue) { +func (s *MetricStore) insertExternalMetric(namespace string, metric external_metrics.ExternalMetricValue) { s.Lock() defer s.Unlock() @@ -131,11 +133,19 @@ func (s *MetricStore) insertExternalMetric(metric external_metrics.ExternalMetri labelsKey := hashLabelMap(metric.MetricLabels) - if metrics, ok := s.externalMetricsStore[metric.MetricName]; ok { - metrics[labelsKey] = storedMetric + if metrics, ok := s.externalMetricsStore[namespace]; ok { + if labels, ok := metrics[metric.MetricName]; ok { + labels[labelsKey] = storedMetric + } else { + metrics[metric.MetricName] = map[string]externalMetricsStoredMetric{ + labelsKey: storedMetric, + } + } } else { - s.externalMetricsStore[metric.MetricName] = map[string]externalMetricsStoredMetric{ - labelsKey: storedMetric, + s.externalMetricsStore[namespace] = map[string]map[string]externalMetricsStoredMetric{ + metric.MetricName: { + labelsKey: storedMetric, + }, } } } @@ -250,10 +260,12 @@ func (s *MetricStore) GetExternalMetric(namespace string, selector labels.Select s.RLock() defer s.RUnlock() - if metrics, ok := s.externalMetricsStore[info.Metric]; ok { - for _, metric := range metrics { - if selector.Matches(labels.Set(metric.Value.MetricLabels)) { - matchedMetrics = append(matchedMetrics, metric.Value) + if metrics, ok := s.externalMetricsStore[namespace]; ok { + if selectors, ok := metrics[info.Metric]; ok { + for _, sel := range selectors { + if selector.Matches(labels.Set(sel.Value.MetricLabels)) { + matchedMetrics = append(matchedMetrics, sel.Value) + } } } } @@ -268,11 +280,13 @@ func (s *MetricStore) ListAllExternalMetrics() []provider.ExternalMetricInfo { metricsInfo := make([]provider.ExternalMetricInfo, 0, len(s.externalMetricsStore)) - for metricName := range s.externalMetricsStore { - info := provider.ExternalMetricInfo{ - Metric: metricName, + for _, metrics := range s.externalMetricsStore { + for metricName := range metrics { + info := provider.ExternalMetricInfo{ + Metric: metricName, + } + metricsInfo = append(metricsInfo, info) } - metricsInfo = append(metricsInfo, info) } return metricsInfo } @@ -306,14 +320,19 @@ func (s *MetricStore) RemoveExpired() { } // cleanup external metrics - for metricName, metrics := range s.externalMetricsStore { - for k, metric := range metrics { - if metric.TTL.Before(time.Now().UTC()) { - delete(metrics, k) + for namespace, metrics := range s.externalMetricsStore { + for metricName, selectors := range metrics { + for k, metric := range selectors { + if metric.TTL.Before(time.Now().UTC()) { + delete(selectors, k) + } + } + if len(selectors) == 0 { + delete(metrics, metricName) } } if len(metrics) == 0 { - delete(s.externalMetricsStore, metricName) + delete(s.externalMetricsStore, namespace) } } } diff --git a/pkg/provider/metrics_store_test.go b/pkg/provider/metrics_store_test.go index f551677e..9d641e3c 100644 --- a/pkg/provider/metrics_store_test.go +++ b/pkg/provider/metrics_store_test.go @@ -26,10 +26,11 @@ func newMetricIdentifier(metricName string) custom_metrics.MetricIdentifier { } func TestInternalMetricStorage(t *testing.T) { var metricStoreTests = []struct { - test string - insert collector.CollectedMetric - list []provider.CustomMetricInfo - byName struct { + test string + insert collector.CollectedMetric + list []provider.CustomMetricInfo + expectedFound bool + byName struct { name types.NamespacedName info provider.CustomMetricInfo } @@ -54,6 +55,7 @@ func TestInternalMetricStorage(t *testing.T) { }, }, }, + expectedFound: true, list: []provider.CustomMetricInfo{ { GroupResource: schema.GroupResource{}, @@ -101,6 +103,7 @@ func TestInternalMetricStorage(t *testing.T) { }, }, }, + expectedFound: true, list: []provider.CustomMetricInfo{ { GroupResource: schema.GroupResource{ @@ -156,6 +159,7 @@ func TestInternalMetricStorage(t *testing.T) { }, }, }, + expectedFound: true, list: []provider.CustomMetricInfo{ { GroupResource: schema.GroupResource{}, @@ -202,6 +206,7 @@ func TestInternalMetricStorage(t *testing.T) { }, }, }, + expectedFound: true, list: []provider.CustomMetricInfo{ { GroupResource: schema.GroupResource{ @@ -243,6 +248,63 @@ func TestInternalMetricStorage(t *testing.T) { }, }, }, + { + test: "get an Ingress metric from wrong namespace", + insert: collector.CollectedMetric{ + Type: autoscalingv2.MetricSourceType("Object"), + Custom: custom_metrics.MetricValue{ + Metric: newMetricIdentifier("metric-per-unit"), + Value: *resource.NewQuantity(0, ""), + DescribedObject: custom_metrics.ObjectReference{ + Name: "metricObject", + Namespace: "right", + Kind: "Ingress", + APIVersion: "extensions/v1beta1", + }, + }, + }, + expectedFound: false, + list: []provider.CustomMetricInfo{ + { + GroupResource: schema.GroupResource{ + Group: "extensions", + Resource: "ingresses", + }, + Namespaced: true, + Metric: "metric-per-unit", + }, + }, + byName: struct { + name types.NamespacedName + info provider.CustomMetricInfo + }{ + name: types.NamespacedName{Name: "metricObject", Namespace: "wrong"}, + info: provider.CustomMetricInfo{ + GroupResource: schema.GroupResource{ + Group: "extensions", + Resource: "ingresses", + }, + Namespaced: true, + Metric: "metric-per-unit", + }, + }, + byLabel: struct { + namespace string + selector labels.Selector + info provider.CustomMetricInfo + }{ + namespace: "wrong", + selector: labels.Everything(), + info: provider.CustomMetricInfo{ + GroupResource: schema.GroupResource{ + Group: "extensions", + Resource: "ingresses", + }, + Namespaced: true, + Metric: "metric-per-unit", + }, + }, + }, } for _, tc := range metricStoreTests { @@ -261,14 +323,14 @@ func TestInternalMetricStorage(t *testing.T) { // Get the metric by name metric := metricsStore.GetMetricsByName(tc.byName.name, tc.byName.info) - if tc.insert.Custom != (custom_metrics.MetricValue{}) { + if tc.expectedFound { require.Equal(t, tc.insert.Custom, *metric) metrics := metricsStore.GetMetricsBySelector(tc.byLabel.namespace, tc.byLabel.selector, tc.byLabel.info) require.Equal(t, tc.insert.Custom, metrics.Items[0]) } else { require.Nil(t, metric) metrics := metricsStore.GetMetricsBySelector(tc.byLabel.namespace, tc.byLabel.selector, tc.byLabel.info) - require.Nil(t, metrics) + require.Len(t, metrics.Items, 0) } }) @@ -707,11 +769,39 @@ func TestExternalMetricStorage(t *testing.T) { namespace string selector labels.Selector info provider.ExternalMetricInfo - }{namespace: "", - selector: labels.Everything(), + }{ + namespace: "", + selector: labels.Everything(), info: provider.ExternalMetricInfo{ Metric: "metric-per-unit", - }}, + }, + }, + }, + { + test: "insert/list/get an external metric with namespace", + insert: collector.CollectedMetric{ + Namespace: "foo", + Type: autoscalingv2.MetricSourceType("External"), + External: external_metrics.ExternalMetricValue{ + MetricName: "metric-per-unit", + Value: *resource.NewQuantity(0, ""), + MetricLabels: map[string]string{"application": "some-app"}, + }, + }, + list: provider.ExternalMetricInfo{ + Metric: "metric-per-unit", + }, + get: struct { + namespace string + selector labels.Selector + info provider.ExternalMetricInfo + }{ + namespace: "foo", + selector: labels.Everything(), + info: provider.ExternalMetricInfo{ + Metric: "metric-per-unit", + }, + }, }, } @@ -740,10 +830,11 @@ func TestExternalMetricStorage(t *testing.T) { func TestMultipleExternalMetricStorage(t *testing.T) { var metricStoreTests = []struct { - test string - insert []collector.CollectedMetric - list provider.ExternalMetricInfo - get struct { + test string + insert []collector.CollectedMetric + expectedIdx int + list []provider.ExternalMetricInfo + get struct { namespace string selector labels.Selector info provider.ExternalMetricInfo @@ -769,18 +860,117 @@ func TestMultipleExternalMetricStorage(t *testing.T) { }, }, }, - list: provider.ExternalMetricInfo{ - Metric: "metric-per-unit", + expectedIdx: 1, + list: []provider.ExternalMetricInfo{ + { + Metric: "metric-per-unit", + }, + }, + get: struct { + namespace string + selector labels.Selector + info provider.ExternalMetricInfo + }{ + namespace: "", + selector: labels.Everything(), + info: provider.ExternalMetricInfo{ + Metric: "metric-per-unit", + }, + }, + }, + { + test: "external metrics are namespaced", + insert: []collector.CollectedMetric{ + { + Namespace: "one", + Type: autoscalingv2.MetricSourceType("External"), + External: external_metrics.ExternalMetricValue{ + MetricName: "metric-per-unit", + Value: *resource.NewQuantity(0, ""), + MetricLabels: map[string]string{"application": "some-app"}, + }, + }, + { + Namespace: "two", + Type: autoscalingv2.MetricSourceType("External"), + External: external_metrics.ExternalMetricValue{ + MetricName: "metric-per-unit", + Value: *resource.NewQuantity(1, ""), + MetricLabels: map[string]string{"application": "some-app"}, + }, + }, + }, + expectedIdx: 1, + list: []provider.ExternalMetricInfo{ + { + Metric: "metric-per-unit", + }, + { + Metric: "metric-per-unit", + }, + }, + get: struct { + namespace string + selector labels.Selector + info provider.ExternalMetricInfo + }{ + namespace: "two", + selector: labels.Everything(), + info: provider.ExternalMetricInfo{ + Metric: "metric-per-unit", + }, + }, + }, + { + test: "external metrics looked up by labels", + insert: []collector.CollectedMetric{ + { + Type: autoscalingv2.MetricSourceType("External"), + External: external_metrics.ExternalMetricValue{ + MetricName: "metric-per-unit", + Value: *resource.NewQuantity(0, ""), + MetricLabels: map[string]string{"application": "some-app-one"}, + }, + }, + { + Type: autoscalingv2.MetricSourceType("External"), + External: external_metrics.ExternalMetricValue{ + MetricName: "metric-per-unit", + Value: *resource.NewQuantity(1, ""), + MetricLabels: map[string]string{"application": "some-app-two"}, + }, + }, + { + Type: autoscalingv2.MetricSourceType("External"), + External: external_metrics.ExternalMetricValue{ + MetricName: "metric-per-unit-x", + Value: *resource.NewQuantity(1, ""), + MetricLabels: map[string]string{"application": "some-app-two"}, + }, + }, + }, + expectedIdx: 0, + list: []provider.ExternalMetricInfo{ + { + Metric: "metric-per-unit", + }, + { + Metric: "metric-per-unit-x", + }, }, get: struct { namespace string selector labels.Selector info provider.ExternalMetricInfo - }{namespace: "", - selector: labels.Everything(), + }{ + namespace: "", + selector: labels.Set(map[string]string{ + "application": "some-app-one", + }).AsSelector(), info: provider.ExternalMetricInfo{ Metric: "metric-per-unit", - }}, + }, + }, }, } @@ -799,12 +989,12 @@ func TestMultipleExternalMetricStorage(t *testing.T) { // Get the metric by name metrics, err := metricsStore.GetExternalMetric(tc.get.namespace, tc.get.selector, tc.get.info) require.NoError(t, err) - require.NotContains(t, metrics.Items, tc.insert[0].External) - require.Contains(t, metrics.Items, tc.insert[1].External) + require.Len(t, metrics.Items, 1) + require.Contains(t, metrics.Items, tc.insert[tc.expectedIdx].External) // List a metric with value metricInfos := metricsStore.ListAllExternalMetrics() - require.Equal(t, tc.list, metricInfos[0]) + require.EqualValues(t, tc.list, metricInfos) }) }