github.com/kubewharf/katalyst-core@v0.5.3/pkg/custom-metric/provider/provider_test.go (about)

     1  /*
     2  Copyright 2022 The Katalyst Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package provider
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"net/http/httptest"
    24  	"strconv"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	v1 "k8s.io/api/core/v1"
    31  	"k8s.io/apimachinery/pkg/api/resource"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/labels"
    34  	"k8s.io/apimachinery/pkg/runtime"
    35  	"k8s.io/apimachinery/pkg/runtime/schema"
    36  	"k8s.io/apimachinery/pkg/types"
    37  	"k8s.io/metrics/pkg/apis/custom_metrics"
    38  	"k8s.io/metrics/pkg/apis/external_metrics"
    39  	"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
    40  
    41  	katalystbase "github.com/kubewharf/katalyst-core/cmd/base"
    42  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    43  	metricconf "github.com/kubewharf/katalyst-core/pkg/config/metric"
    44  	"github.com/kubewharf/katalyst-core/pkg/custom-metric/store"
    45  	"github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data"
    46  	"github.com/kubewharf/katalyst-core/pkg/custom-metric/store/local"
    47  	"github.com/kubewharf/katalyst-core/pkg/custom-metric/store/remote"
    48  	"github.com/kubewharf/katalyst-core/pkg/util/native"
    49  )
    50  
    51  func generateStorePodMeta(namespace, name, nameLabel string, port int32) *metav1.PartialObjectMetadata {
    52  	return &metav1.PartialObjectMetadata{
    53  		TypeMeta: metav1.TypeMeta{
    54  			APIVersion: "v1",
    55  			Kind:       "Pod",
    56  		},
    57  		ObjectMeta: metav1.ObjectMeta{
    58  			Namespace: namespace,
    59  			Name:      name,
    60  			Labels: map[string]string{
    61  				"test": "local-store",
    62  				"name": nameLabel,
    63  			},
    64  		},
    65  	}
    66  }
    67  
    68  func generateStoreNodeMeta(name, nameLabel string) *metav1.PartialObjectMetadata {
    69  	return &metav1.PartialObjectMetadata{
    70  		TypeMeta: metav1.TypeMeta{
    71  			APIVersion: "v1",
    72  			Kind:       "Node",
    73  		},
    74  		ObjectMeta: metav1.ObjectMeta{
    75  			Name: name,
    76  			Labels: map[string]string{
    77  				"name": nameLabel,
    78  			},
    79  		},
    80  	}
    81  }
    82  
    83  func generateStorePod(namespace, name, nameLabel string, port int32) *v1.Pod {
    84  	return &v1.Pod{
    85  		ObjectMeta: metav1.ObjectMeta{
    86  			Namespace: namespace,
    87  			Name:      name,
    88  			Labels: map[string]string{
    89  				"test": "local-store",
    90  				"name": nameLabel,
    91  			},
    92  		},
    93  		Spec: v1.PodSpec{
    94  			Containers: []v1.Container{
    95  				{
    96  					Name: "pod-container",
    97  					Ports: []v1.ContainerPort{
    98  						{
    99  							Name:     native.ContainerMetricStorePortName,
   100  							HostPort: port,
   101  						},
   102  					},
   103  				},
   104  			},
   105  		},
   106  		Status: v1.PodStatus{
   107  			HostIP: "127.0.0.1",
   108  			ContainerStatuses: []v1.ContainerStatus{
   109  				{
   110  					Ready: true,
   111  				},
   112  			},
   113  		},
   114  	}
   115  }
   116  
   117  func TestWithLocalStore(t *testing.T) {
   118  	t.Parallel()
   119  
   120  	ctx := context.Background()
   121  
   122  	p1 := generateStorePodMeta("ns-1", "pod-1", "full_metric_with_conflict_time", 11)
   123  	p2 := generateStorePodMeta("ns-2", "pod-2", "full_metric_with_multiple_data", 11)
   124  	p3 := generateStorePodMeta("ns-3", "pod-3", "full_metric_with_multiple_label", 33)
   125  	n1 := generateStoreNodeMeta("node-1", "full_metric_with_node")
   126  
   127  	baseCtx, err := katalystbase.GenerateFakeGenericContext(nil, nil, nil, []runtime.Object{p1, p2, p3, n1})
   128  	assert.NoError(t, err)
   129  
   130  	genericConf := &metricconf.GenericMetricConfiguration{
   131  		OutOfDataPeriod: time.Second * 10,
   132  	}
   133  	storeConf := &metricconf.StoreConfiguration{
   134  		ServiceDiscoveryConf: &generic.ServiceDiscoveryConf{
   135  			PodSinglePortSDConf: &generic.PodSinglePortSDConf{
   136  				PortName: native.ContainerMetricStorePortName,
   137  				PodLister: labels.SelectorFromSet(map[string]string{
   138  					"test": "local-store",
   139  				}),
   140  			},
   141  		},
   142  		PurgePeriod:    time.Second * 3,
   143  		GCPeriod:       time.Second * 3,
   144  		IndexLabelKeys: []string{"name"},
   145  	}
   146  
   147  	s, err := local.NewLocalMemoryMetricStore(ctx, baseCtx, genericConf, storeConf)
   148  	assert.NoError(t, err)
   149  
   150  	baseCtx.StartInformer(ctx)
   151  	err = s.Start()
   152  	assert.NoError(t, err)
   153  
   154  	p := NewMetricProviderImp(ctx, baseCtx, s)
   155  	testProvider(t, p, s, ctx, baseCtx, genericConf, storeConf)
   156  }
   157  
   158  func TestWithRemoteStoreOne(t *testing.T) {
   159  	t.Parallel()
   160  
   161  	testWithRemoteStoreWithIndex(t, []int{1})
   162  }
   163  
   164  func TestWithRemoteStoreTwo(t *testing.T) {
   165  	t.Parallel()
   166  
   167  	testWithRemoteStoreWithIndex(t, []int{1, 2})
   168  }
   169  
   170  func TestWithRemoteStoreThree(t *testing.T) {
   171  	t.Parallel()
   172  
   173  	testWithRemoteStoreWithIndex(t, []int{1, 2, 3})
   174  }
   175  
   176  func testWithRemoteStoreWithIndex(t *testing.T, index []int) {
   177  	ctx := context.Background()
   178  
   179  	genericConf := &metricconf.GenericMetricConfiguration{
   180  		OutOfDataPeriod: time.Second * 10,
   181  	}
   182  	storeConf := &metricconf.StoreConfiguration{
   183  		ServiceDiscoveryConf: &generic.ServiceDiscoveryConf{
   184  			PodSinglePortSDConf: &generic.PodSinglePortSDConf{
   185  				PortName: native.ContainerMetricStorePortName,
   186  				PodLister: labels.SelectorFromSet(map[string]string{
   187  					"test": "local-store",
   188  				}),
   189  			},
   190  		},
   191  		StoreServerReplicaTotal: len(index),
   192  		GCPeriod:                time.Second * 3,
   193  		PurgePeriod:             time.Second * 3,
   194  		IndexLabelKeys:          []string{"name"},
   195  	}
   196  
   197  	lp1 := generateStorePodMeta("ns-1", "pod-1", "full_metric_with_conflict_time", 11)
   198  	lp2 := generateStorePodMeta("ns-2", "pod-2", "full_metric_with_multiple_data", 22)
   199  	lp3 := generateStorePodMeta("ns-3", "pod-3", "full_metric_with_multiple_label", 33)
   200  	ln1 := generateStoreNodeMeta("node-1", "full_metric_with_node")
   201  
   202  	var podList []runtime.Object
   203  	for i := range index {
   204  		mux := http.NewServeMux()
   205  
   206  		server := httptest.NewServer(mux)
   207  		t.Logf("server url %v\n", server.URL)
   208  
   209  		urlList := strings.Split(server.URL, ":")
   210  		assert.Equal(t, len(urlList), 3)
   211  		port, err := strconv.Atoi(strings.TrimSpace(urlList[2]))
   212  		assert.NoError(t, err)
   213  
   214  		baseCtx, err := katalystbase.GenerateFakeGenericContext(nil, nil, nil, []runtime.Object{lp1, lp2, lp3, ln1})
   215  		assert.NoError(t, err)
   216  		baseCtx.Handler = mux
   217  
   218  		l, err := local.NewLocalMemoryMetricStore(ctx, baseCtx, genericConf, storeConf)
   219  		assert.NoError(t, err)
   220  		baseCtx.StartInformer(ctx)
   221  
   222  		err = l.Start()
   223  		assert.NoError(t, err)
   224  		l.(*local.LocalMemoryMetricStore).Serve(baseCtx.Handler.(*http.ServeMux))
   225  
   226  		p := generateStorePod("ns-fake", fmt.Sprintf("pod-r-%v", i), fmt.Sprintf("pod-r-%v", i), int32(port))
   227  		podList = append(podList, p)
   228  	}
   229  
   230  	baseCtx, err := katalystbase.GenerateFakeGenericContext(podList, nil, nil)
   231  	assert.NoError(t, err)
   232  
   233  	r, err := remote.NewRemoteMemoryMetricStore(ctx, baseCtx, genericConf, storeConf)
   234  	assert.NoError(t, err)
   235  	baseCtx.StartInformer(ctx)
   236  
   237  	err = r.Start()
   238  	assert.NoError(t, err)
   239  
   240  	p := NewMetricProviderImp(ctx, baseCtx, r)
   241  	testProvider(t, p, r, ctx, baseCtx, genericConf, storeConf)
   242  }
   243  
   244  func testProvider(t *testing.T, p MetricProvider, s store.MetricStore, ctx context.Context, baseCtx *katalystbase.GenericContext,
   245  	genericConf *metricconf.GenericMetricConfiguration, storeConf *metricconf.StoreConfiguration,
   246  ) {
   247  	var err error
   248  
   249  	podGR := schema.GroupVersionResource{Version: "v1", Resource: "pods"}.GroupResource()
   250  	nodeGR := schema.GroupVersionResource{Version: "v1", Resource: "nodes"}.GroupResource()
   251  
   252  	now := time.Now().Add(time.Second * 10)
   253  	err = s.InsertMetric([]*data.MetricSeries{
   254  		{
   255  			Name: "none_namespace_metric",
   256  			Labels: map[string]string{
   257  				"labels-1":      "key-1",
   258  				"selector_name": "none_namespace_metric",
   259  			},
   260  			Series: []*data.MetricData{
   261  				{
   262  					Data:      1,
   263  					Timestamp: now.UnixMilli(),
   264  				},
   265  				{
   266  					Data:      2,
   267  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() + time.Second.Milliseconds()*5,
   268  				},
   269  			},
   270  		},
   271  		{
   272  			Name: "none_namespace_metric_all_timeout",
   273  			Labels: map[string]string{
   274  				"labels-2":      "key-2",
   275  				"selector_name": "none_namespace_metric_all_timeout",
   276  			},
   277  			Series: []*data.MetricData{
   278  				{
   279  					Data:      2,
   280  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() + time.Second.Milliseconds()*5,
   281  				},
   282  			},
   283  		},
   284  		{
   285  			Name: "none_object_metric",
   286  			Labels: map[string]string{
   287  				"selector_name": "none_object_metric",
   288  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace): "ns-1",
   289  			},
   290  			Series: []*data.MetricData{
   291  				{
   292  					Data:      19,
   293  					Timestamp: now.UnixMilli(),
   294  				},
   295  				{
   296  					Data:      23,
   297  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds(),
   298  				},
   299  			},
   300  		},
   301  		{
   302  			Name: "invalid_object_metric",
   303  			Labels: map[string]string{
   304  				"selector_name": "invalid_object_metric",
   305  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace): "ns-1",
   306  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):    "pods",
   307  			},
   308  			Series: []*data.MetricData{
   309  				{
   310  					Data:      19,
   311  					Timestamp: now.UnixMilli(),
   312  				},
   313  			},
   314  		},
   315  		{
   316  			Name: "object_metric_with_unsupported_obj",
   317  			Labels: map[string]string{
   318  				"selector_name": "object_metric_with_unsupported_obj",
   319  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-1",
   320  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "deployment",
   321  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "dp-1",
   322  			},
   323  			Series: []*data.MetricData{
   324  				{
   325  					Data:      31,
   326  					Timestamp: now.UnixMilli(),
   327  				},
   328  			},
   329  		},
   330  		{
   331  			Name: "full_metric_with_conflict_time",
   332  			Labels: map[string]string{
   333  				"selector_name": "full_metric_with_conflict_time",
   334  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-1",
   335  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "pods",
   336  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "pod-1",
   337  			},
   338  			Series: []*data.MetricData{
   339  				{
   340  					Data:      31,
   341  					Timestamp: now.UnixMilli(),
   342  				},
   343  				{
   344  					Data:      21,
   345  					Timestamp: now.UnixMilli(),
   346  				},
   347  			},
   348  		},
   349  		{
   350  			Name: "full_metric_with_multiple_data",
   351  			Labels: map[string]string{
   352  				"selector_name": "full_metric_with_multiple_data",
   353  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-2",
   354  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "pods",
   355  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "pod-2",
   356  			},
   357  			Series: []*data.MetricData{
   358  				{
   359  					Data:      31,
   360  					Timestamp: now.UnixMilli(),
   361  				},
   362  				{
   363  					Data:      44,
   364  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*3,
   365  				},
   366  			},
   367  		},
   368  		{
   369  			Name: "full_metric_with_multiple_data",
   370  			Labels: map[string]string{
   371  				"selector_name": "full_metric_with_multiple_data",
   372  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-2",
   373  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "pods",
   374  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "pod-2",
   375  			},
   376  			Series: []*data.MetricData{
   377  				{
   378  					Data:      34,
   379  					Timestamp: now.UnixMilli(),
   380  				},
   381  			},
   382  		},
   383  		{
   384  			Name: "full_metric_with_multiple_data",
   385  			Labels: map[string]string{
   386  				"selector_name": "full_metric_with_multiple_data",
   387  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-2",
   388  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "pods",
   389  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "pod-2",
   390  			},
   391  			Series: []*data.MetricData{
   392  				{
   393  					Data:      86,
   394  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2,
   395  				},
   396  			},
   397  		},
   398  		{
   399  			Name: "full_metric_with_multiple_label",
   400  			Labels: map[string]string{
   401  				"selector_container": "container1",
   402  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-3",
   403  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "pods",
   404  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "pod-3",
   405  			},
   406  			Series: []*data.MetricData{
   407  				{
   408  					Data:      0,
   409  					Timestamp: now.UnixMilli(),
   410  				},
   411  				{
   412  					Data:      50,
   413  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2,
   414  				},
   415  				{
   416  					Data:      100,
   417  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*4,
   418  				},
   419  			},
   420  		},
   421  		{
   422  			Name: "full_metric_with_multiple_label",
   423  			Labels: map[string]string{
   424  				"selector_container": "container2",
   425  				fmt.Sprintf("%v", data.CustomMetricLabelKeyNamespace):  "ns-3",
   426  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "pods",
   427  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "pod-3",
   428  			},
   429  			Series: []*data.MetricData{
   430  				{
   431  					Data:      100,
   432  					Timestamp: now.UnixMilli(),
   433  				},
   434  				{
   435  					Data:      150,
   436  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2,
   437  				},
   438  				{
   439  					Data:      200,
   440  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*4,
   441  				},
   442  			},
   443  		},
   444  		{
   445  			Name: "full_metric_with_node",
   446  			Labels: map[string]string{
   447  				"selector_name": "full_metric_with_node",
   448  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObject):     "nodes",
   449  				fmt.Sprintf("%v", data.CustomMetricLabelKeyObjectName): "node-1",
   450  			},
   451  			Series: []*data.MetricData{
   452  				{
   453  					Data:      73,
   454  					Timestamp: now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2,
   455  				},
   456  			},
   457  		},
   458  	})
   459  	assert.NoError(t, err)
   460  
   461  	var (
   462  		oneMetric   *custom_metrics.MetricValue
   463  		batchMetric *custom_metrics.MetricValueList
   464  		metricInfo  []provider.CustomMetricInfo
   465  
   466  		batchExternal *external_metrics.ExternalMetricValueList
   467  		externalInfo  []provider.ExternalMetricInfo
   468  	)
   469  
   470  	t.Log("#### 1.1: ListAllMetrics")
   471  
   472  	metricInfo = p.ListAllMetrics()
   473  	assert.Equal(t, 4, len(metricInfo))
   474  	assert.ElementsMatch(t, []provider.CustomMetricInfo{
   475  		{
   476  			GroupResource: podGR,
   477  			Namespaced:    true,
   478  			Metric:        "full_metric_with_conflict_time",
   479  		},
   480  		{
   481  			GroupResource: podGR,
   482  			Namespaced:    true,
   483  			Metric:        "full_metric_with_multiple_data",
   484  		},
   485  		{
   486  			GroupResource: podGR,
   487  			Namespaced:    true,
   488  			Metric:        "full_metric_with_multiple_label",
   489  		},
   490  		{
   491  			GroupResource: nodeGR,
   492  			Namespaced:    false,
   493  			Metric:        "full_metric_with_node",
   494  		},
   495  	}, metricInfo)
   496  
   497  	t.Log("#### 1.2.1: GetMetricByName for node")
   498  
   499  	oneMetric, err = p.GetMetricByName(ctx, types.NamespacedName{
   500  		Name: "node-1",
   501  	}, provider.CustomMetricInfo{
   502  		GroupResource: nodeGR,
   503  		Namespaced:    false,
   504  		Metric:        "full_metric_with_node",
   505  	}, labels.Everything())
   506  	assert.NoError(t, err)
   507  	assert.Equal(t, &custom_metrics.MetricValue{
   508  		DescribedObject: custom_metrics.ObjectReference{
   509  			Name: "node-1",
   510  			Kind: "nodes",
   511  		},
   512  		Metric: custom_metrics.MetricIdentifier{
   513  			Name: "full_metric_with_node",
   514  			Selector: &metav1.LabelSelector{
   515  				MatchLabels: map[string]string{
   516  					"name": "full_metric_with_node",
   517  				},
   518  			},
   519  		},
   520  		Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2)),
   521  		Value:     resource.MustParse("73"),
   522  	}, oneMetric)
   523  
   524  	t.Log("#### 1.2.2: GetMetricByName for pod")
   525  
   526  	oneMetric, err = p.GetMetricByName(ctx, types.NamespacedName{
   527  		Namespace: "ns-1",
   528  		Name:      "pod-1",
   529  	}, provider.CustomMetricInfo{
   530  		GroupResource: podGR,
   531  		Namespaced:    false,
   532  		Metric:        "full_metric_with_conflict_time",
   533  	}, labels.SelectorFromSet(labels.Set(map[string]string{
   534  		"name": "full_metric_with_conflict_time",
   535  	})))
   536  	assert.NoError(t, err)
   537  	assert.Equal(t, &custom_metrics.MetricValue{
   538  		DescribedObject: custom_metrics.ObjectReference{
   539  			Namespace: "ns-1",
   540  			Name:      "pod-1",
   541  			Kind:      "pods",
   542  		},
   543  		Metric: custom_metrics.MetricIdentifier{
   544  			Name: "full_metric_with_conflict_time",
   545  			Selector: &metav1.LabelSelector{
   546  				MatchLabels: map[string]string{
   547  					"name": "full_metric_with_conflict_time",
   548  				},
   549  			},
   550  		},
   551  		Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   552  		Value:     resource.MustParse("31"),
   553  	}, oneMetric)
   554  
   555  	t.Log("#### 1.3.1: GetMetricBySelector empty ns")
   556  
   557  	batchMetric, err = p.GetMetricBySelector(ctx, "", labels.Everything(), provider.CustomMetricInfo{GroupResource: nodeGR}, labels.Everything())
   558  	assert.NoError(t, err)
   559  	assert.Equal(t, 1, len(batchMetric.Items))
   560  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   561  		{
   562  			DescribedObject: custom_metrics.ObjectReference{
   563  				Name: "node-1",
   564  				Kind: "nodes",
   565  			},
   566  			Metric: custom_metrics.MetricIdentifier{
   567  				Name: "full_metric_with_node",
   568  				Selector: &metav1.LabelSelector{
   569  					MatchLabels: map[string]string{
   570  						"name": "full_metric_with_node",
   571  					},
   572  				},
   573  			},
   574  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2)),
   575  			Value:     resource.MustParse("73"),
   576  		},
   577  	}, batchMetric.Items)
   578  
   579  	t.Log("#### 1.3.2: GetMetricBySelector byLabel")
   580  	batchMetric, err = p.GetMetricBySelector(ctx, "ns-2",
   581  		labels.SelectorFromSet(labels.Set{"name": "full_metric_with_multiple_data"}),
   582  		provider.CustomMetricInfo{Metric: "full_metric_with_multiple_data", GroupResource: podGR}, labels.Everything())
   583  	assert.NoError(t, err)
   584  	assert.Equal(t, 3, len(batchMetric.Items))
   585  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   586  		{
   587  			DescribedObject: custom_metrics.ObjectReference{
   588  				Namespace: "ns-2",
   589  				Name:      "pod-2",
   590  				Kind:      "pods",
   591  			},
   592  			Metric: custom_metrics.MetricIdentifier{
   593  				Name: "full_metric_with_multiple_data",
   594  				Selector: &metav1.LabelSelector{
   595  					MatchLabels: map[string]string{
   596  						"name": "full_metric_with_multiple_data",
   597  					},
   598  				},
   599  			},
   600  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   601  			Value:     resource.MustParse("31"),
   602  		},
   603  		{
   604  			DescribedObject: custom_metrics.ObjectReference{
   605  				Namespace: "ns-2",
   606  				Name:      "pod-2",
   607  				Kind:      "pods",
   608  			},
   609  			Metric: custom_metrics.MetricIdentifier{
   610  				Name: "full_metric_with_multiple_data",
   611  				Selector: &metav1.LabelSelector{
   612  					MatchLabels: map[string]string{
   613  						"name": "full_metric_with_multiple_data",
   614  					},
   615  				},
   616  			},
   617  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*3)),
   618  			Value:     resource.MustParse("44"),
   619  		},
   620  		{
   621  			DescribedObject: custom_metrics.ObjectReference{
   622  				Namespace: "ns-2",
   623  				Name:      "pod-2",
   624  				Kind:      "pods",
   625  			},
   626  			Metric: custom_metrics.MetricIdentifier{
   627  				Name: "full_metric_with_multiple_data",
   628  				Selector: &metav1.LabelSelector{
   629  					MatchLabels: map[string]string{
   630  						"name": "full_metric_with_multiple_data",
   631  					},
   632  				},
   633  			},
   634  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2)),
   635  			Value:     resource.MustParse("86"),
   636  		},
   637  	}, batchMetric.Items)
   638  
   639  	t.Log("#### 1.3.3: GetMetricBySelector ns-1")
   640  
   641  	batchMetric, err = p.GetMetricBySelector(ctx, "ns-1", labels.Everything(), provider.CustomMetricInfo{GroupResource: podGR}, labels.Everything())
   642  	assert.NoError(t, err)
   643  	assert.Equal(t, 1, len(batchMetric.Items))
   644  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   645  		{
   646  			DescribedObject: custom_metrics.ObjectReference{
   647  				Namespace: "ns-1",
   648  				Name:      "pod-1",
   649  				Kind:      "pods",
   650  			},
   651  			Metric: custom_metrics.MetricIdentifier{
   652  				Name: "full_metric_with_conflict_time",
   653  				Selector: &metav1.LabelSelector{
   654  					MatchLabels: map[string]string{
   655  						"name": "full_metric_with_conflict_time",
   656  					},
   657  				},
   658  			},
   659  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   660  			Value:     resource.MustParse("31"),
   661  		},
   662  	}, batchMetric.Items)
   663  
   664  	t.Log("#### 1.3.4: GetMetricBySelector ns-2")
   665  
   666  	batchMetric, err = p.GetMetricBySelector(ctx, "ns-2", labels.Everything(), provider.CustomMetricInfo{GroupResource: podGR}, labels.Everything())
   667  	assert.NoError(t, err)
   668  	assert.Equal(t, 3, len(batchMetric.Items))
   669  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   670  		{
   671  			DescribedObject: custom_metrics.ObjectReference{
   672  				Namespace: "ns-2",
   673  				Name:      "pod-2",
   674  				Kind:      "pods",
   675  			},
   676  			Metric: custom_metrics.MetricIdentifier{
   677  				Name: "full_metric_with_multiple_data",
   678  				Selector: &metav1.LabelSelector{
   679  					MatchLabels: map[string]string{
   680  						"name": "full_metric_with_multiple_data",
   681  					},
   682  				},
   683  			},
   684  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   685  			Value:     resource.MustParse("31"),
   686  		},
   687  		{
   688  			DescribedObject: custom_metrics.ObjectReference{
   689  				Namespace: "ns-2",
   690  				Name:      "pod-2",
   691  				Kind:      "pods",
   692  			},
   693  			Metric: custom_metrics.MetricIdentifier{
   694  				Name: "full_metric_with_multiple_data",
   695  				Selector: &metav1.LabelSelector{
   696  					MatchLabels: map[string]string{
   697  						"name": "full_metric_with_multiple_data",
   698  					},
   699  				},
   700  			},
   701  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*3)),
   702  			Value:     resource.MustParse("44"),
   703  		},
   704  		{
   705  			DescribedObject: custom_metrics.ObjectReference{
   706  				Namespace: "ns-2",
   707  				Name:      "pod-2",
   708  				Kind:      "pods",
   709  			},
   710  			Metric: custom_metrics.MetricIdentifier{
   711  				Name: "full_metric_with_multiple_data",
   712  				Selector: &metav1.LabelSelector{
   713  					MatchLabels: map[string]string{
   714  						"name": "full_metric_with_multiple_data",
   715  					},
   716  				},
   717  			},
   718  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() - time.Second.Milliseconds()*2)),
   719  			Value:     resource.MustParse("86"),
   720  		},
   721  	}, batchMetric.Items)
   722  
   723  	t.Log("#### 1.4: ListAllExternalMetrics")
   724  
   725  	externalInfo = p.ListAllExternalMetrics()
   726  	assert.Equal(t, 3, len(externalInfo))
   727  	assert.ElementsMatch(t, []provider.ExternalMetricInfo{
   728  		{
   729  			Metric: "none_namespace_metric",
   730  		},
   731  		{
   732  			Metric: "none_namespace_metric_all_timeout",
   733  		},
   734  		{
   735  			Metric: "none_object_metric",
   736  		},
   737  	}, externalInfo)
   738  
   739  	t.Log("#### 1.5.1: GetExternalMetric empty ns")
   740  
   741  	batchExternal, err = p.GetExternalMetric(ctx, "", labels.Everything(), provider.ExternalMetricInfo{})
   742  	assert.NoError(t, err)
   743  	assert.Equal(t, 3, len(batchExternal.Items))
   744  	assert.ElementsMatch(t, []external_metrics.ExternalMetricValue{
   745  		{
   746  			MetricName: "none_namespace_metric",
   747  			MetricLabels: map[string]string{
   748  				"name": "none_namespace_metric",
   749  			},
   750  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   751  			Value:     resource.MustParse("1"),
   752  		},
   753  		{
   754  			MetricName: "none_namespace_metric",
   755  			MetricLabels: map[string]string{
   756  				"name": "none_namespace_metric",
   757  			},
   758  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() + time.Second.Milliseconds()*5)),
   759  			Value:     resource.MustParse("2"),
   760  		},
   761  		{
   762  			MetricName: "none_namespace_metric_all_timeout",
   763  			MetricLabels: map[string]string{
   764  				"name": "none_namespace_metric_all_timeout",
   765  			},
   766  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds() + time.Second.Milliseconds()*5)),
   767  			Value:     resource.MustParse("2"),
   768  		},
   769  	}, batchExternal.Items)
   770  
   771  	t.Log("#### 1.5.2: GetExternalMetric ns-1")
   772  
   773  	batchExternal, err = p.GetExternalMetric(ctx, "ns-1", labels.Everything(), provider.ExternalMetricInfo{})
   774  	assert.NoError(t, err)
   775  	assert.Equal(t, 2, len(batchExternal.Items))
   776  	assert.ElementsMatch(t, []external_metrics.ExternalMetricValue{
   777  		{
   778  			MetricName: "none_object_metric",
   779  			MetricLabels: map[string]string{
   780  				"name": "none_object_metric",
   781  			},
   782  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   783  			Value:     resource.MustParse("19"),
   784  		},
   785  		{
   786  			MetricName: "none_object_metric",
   787  			MetricLabels: map[string]string{
   788  				"name": "none_object_metric",
   789  			},
   790  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli() - genericConf.OutOfDataPeriod.Milliseconds())),
   791  			Value:     resource.MustParse("23"),
   792  		},
   793  	}, batchExternal.Items)
   794  
   795  	t.Log("#### 1.6.1 GetAggregatedMetric pod-3")
   796  	metric, err := p.GetMetricBySelector(ctx, "ns-3", labels.Everything(),
   797  		provider.CustomMetricInfo{GroupResource: podGR, Metric: "full_metric_with_multiple_label_agg_avg"},
   798  		labels.Everything())
   799  	assert.NoError(t, err)
   800  	assert.Equal(t, 1, len(metric.Items))
   801  	windowSeconds := int64(14)
   802  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   803  		{
   804  			DescribedObject: custom_metrics.ObjectReference{
   805  				Namespace: "ns-3",
   806  				Name:      "pod-3",
   807  				Kind:      "pods",
   808  			},
   809  			Metric: custom_metrics.MetricIdentifier{
   810  				Name:     "full_metric_with_multiple_label_agg_avg",
   811  				Selector: &metav1.LabelSelector{},
   812  			},
   813  			Timestamp:     metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   814  			Value:         resource.MustParse("100"),
   815  			WindowSeconds: &windowSeconds,
   816  		},
   817  	}, metric.Items)
   818  
   819  	t.Log("#### 1.6.2 GetGroupedAggregatedMetric pod-3")
   820  	metric, err = p.GetMetricBySelector(ctx, "ns-3", labels.Everything(),
   821  		provider.CustomMetricInfo{GroupResource: podGR, Metric: "full_metric_with_multiple_label_agg_avg"},
   822  		labels.SelectorFromSet(labels.Set{"groupBy": "container"}))
   823  	assert.NoError(t, err)
   824  	assert.Equal(t, 2, len(metric.Items))
   825  	windowSeconds = int64(14)
   826  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   827  		{
   828  			DescribedObject: custom_metrics.ObjectReference{
   829  				Namespace: "ns-3",
   830  				Name:      "pod-3",
   831  				Kind:      "pods",
   832  			},
   833  			Metric: custom_metrics.MetricIdentifier{
   834  				Name: "full_metric_with_multiple_label_agg_avg",
   835  				Selector: &metav1.LabelSelector{
   836  					MatchLabels: map[string]string{
   837  						"container": "container1",
   838  					},
   839  				},
   840  			},
   841  			Timestamp:     metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   842  			Value:         resource.MustParse("50"),
   843  			WindowSeconds: &windowSeconds,
   844  		},
   845  		{
   846  			DescribedObject: custom_metrics.ObjectReference{
   847  				Namespace: "ns-3",
   848  				Name:      "pod-3",
   849  				Kind:      "pods",
   850  			},
   851  			Metric: custom_metrics.MetricIdentifier{
   852  				Name: "full_metric_with_multiple_label_agg_avg",
   853  				Selector: &metav1.LabelSelector{
   854  					MatchLabels: map[string]string{
   855  						"container": "container2",
   856  					},
   857  				},
   858  			},
   859  			Timestamp:     metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   860  			Value:         resource.MustParse("150"),
   861  			WindowSeconds: &windowSeconds,
   862  		},
   863  	}, metric.Items)
   864  
   865  	// sleep a while to trigger gc
   866  	time.Sleep(time.Second * 20)
   867  
   868  	t.Log("#### 2.1: ListAllMetrics")
   869  
   870  	metricInfo = p.ListAllMetrics()
   871  	assert.Equal(t, 4, len(metricInfo))
   872  	assert.ElementsMatch(t, []provider.CustomMetricInfo{
   873  		{
   874  			GroupResource: podGR,
   875  			Namespaced:    true,
   876  			Metric:        "full_metric_with_conflict_time",
   877  		},
   878  		{
   879  			GroupResource: podGR,
   880  			Namespaced:    true,
   881  			Metric:        "full_metric_with_multiple_data",
   882  		},
   883  		{
   884  			GroupResource: podGR,
   885  			Namespaced:    true,
   886  			Metric:        "full_metric_with_multiple_label",
   887  		},
   888  		{
   889  			GroupResource: nodeGR,
   890  			Namespaced:    false,
   891  			Metric:        "full_metric_with_node",
   892  		},
   893  	}, metricInfo)
   894  
   895  	t.Log("#### 2.3.1: GetMetricBySelector empty ns")
   896  
   897  	batchMetric, err = p.GetMetricBySelector(ctx, "", labels.Everything(), provider.CustomMetricInfo{GroupResource: podGR}, labels.Everything())
   898  	assert.NoError(t, err)
   899  	assert.Equal(t, 0, len(batchMetric.Items))
   900  
   901  	t.Log("#### 2.3.2: GetMetricBySelector ns-1")
   902  
   903  	batchMetric, err = p.GetMetricBySelector(ctx, "ns-1", labels.Everything(), provider.CustomMetricInfo{GroupResource: podGR}, labels.Everything())
   904  	assert.NoError(t, err)
   905  	assert.Equal(t, 1, len(batchMetric.Items))
   906  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   907  		{
   908  			DescribedObject: custom_metrics.ObjectReference{
   909  				Namespace: "ns-1",
   910  				Name:      "pod-1",
   911  				Kind:      "pods",
   912  			},
   913  			Metric: custom_metrics.MetricIdentifier{
   914  				Name: "full_metric_with_conflict_time",
   915  				Selector: &metav1.LabelSelector{
   916  					MatchLabels: map[string]string{
   917  						"name": "full_metric_with_conflict_time",
   918  					},
   919  				},
   920  			},
   921  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   922  			Value:     resource.MustParse("31"),
   923  		},
   924  	}, batchMetric.Items)
   925  
   926  	t.Log("#### 2.3.3: GetMetricBySelector ns-2")
   927  
   928  	batchMetric, err = p.GetMetricBySelector(ctx, "ns-2", labels.Everything(), provider.CustomMetricInfo{GroupResource: podGR}, labels.Everything())
   929  	assert.NoError(t, err)
   930  	assert.Equal(t, 1, len(batchMetric.Items))
   931  	assert.ElementsMatch(t, []custom_metrics.MetricValue{
   932  		{
   933  			DescribedObject: custom_metrics.ObjectReference{
   934  				Namespace: "ns-2",
   935  				Name:      "pod-2",
   936  				Kind:      "pods",
   937  			},
   938  			Metric: custom_metrics.MetricIdentifier{
   939  				Name: "full_metric_with_multiple_data",
   940  				Selector: &metav1.LabelSelector{
   941  					MatchLabels: map[string]string{
   942  						"name": "full_metric_with_multiple_data",
   943  					},
   944  				},
   945  			},
   946  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   947  			Value:     resource.MustParse("31"),
   948  		},
   949  	}, batchMetric.Items)
   950  
   951  	t.Log("#### 2.4: ListAllExternalMetrics")
   952  
   953  	externalInfo = p.ListAllExternalMetrics()
   954  	assert.Equal(t, 3, len(externalInfo))
   955  	assert.ElementsMatch(t, []provider.ExternalMetricInfo{
   956  		{
   957  			Metric: "none_namespace_metric",
   958  		},
   959  		{
   960  			Metric: "none_object_metric",
   961  		},
   962  		{
   963  			Metric: "none_namespace_metric_all_timeout",
   964  		},
   965  	}, externalInfo)
   966  
   967  	t.Log("#### 2.5.1: GetExternalMetric empty ns")
   968  
   969  	batchExternal, err = p.GetExternalMetric(ctx, "", labels.Everything(), provider.ExternalMetricInfo{})
   970  	assert.NoError(t, err)
   971  	assert.Equal(t, 1, len(batchExternal.Items))
   972  	assert.ElementsMatch(t, []external_metrics.ExternalMetricValue{
   973  		{
   974  			MetricName: "none_namespace_metric",
   975  			MetricLabels: map[string]string{
   976  				"name": "none_namespace_metric",
   977  			},
   978  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   979  			Value:     resource.MustParse("1"),
   980  		},
   981  	}, batchExternal.Items)
   982  
   983  	t.Log("#### 2.5.2: GetExternalMetric ns-1")
   984  
   985  	batchExternal, err = p.GetExternalMetric(ctx, "ns-1", labels.Everything(), provider.ExternalMetricInfo{})
   986  	assert.NoError(t, err)
   987  	assert.Equal(t, 1, len(batchExternal.Items))
   988  	assert.ElementsMatch(t, []external_metrics.ExternalMetricValue{
   989  		{
   990  			MetricName: "none_object_metric",
   991  			MetricLabels: map[string]string{
   992  				"name": "none_object_metric",
   993  			},
   994  			Timestamp: metav1.NewTime(time.UnixMilli(now.UnixMilli())),
   995  			Value:     resource.MustParse("19"),
   996  		},
   997  	}, batchExternal.Items)
   998  
   999  	_ = s.Stop()
  1000  }