github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/orm/resourceprovider_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 orm
    18  
    19  import (
    20  	"context"
    21  	"io/ioutil"
    22  	"os"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"k8s.io/apimachinery/pkg/api/resource"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	v1 "k8s.io/api/core/v1"
    32  	v12 "k8s.io/kubelet/pkg/apis/podresources/v1"
    33  	pluginapi "k8s.io/kubelet/pkg/apis/resourceplugin/v1alpha1"
    34  	"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
    35  
    36  	"github.com/kubewharf/katalyst-core/pkg/agent/orm/endpoint"
    37  	"github.com/kubewharf/katalyst-core/pkg/agent/orm/metamanager"
    38  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    39  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    40  )
    41  
    42  func TestGetTopologyAwareResources(t *testing.T) {
    43  	t.Parallel()
    44  
    45  	ckDir, err := ioutil.TempDir("", "checkpoint-Test")
    46  	assert.NoError(t, err)
    47  	defer func() { _ = os.RemoveAll(ckDir) }()
    48  
    49  	conf := generateTestConfiguration(ckDir)
    50  	metaServer, err := generateTestMetaServer(conf, []*v1.Pod{})
    51  	assert.NoError(t, err)
    52  	metamanager := metamanager.NewManager(metrics.DummyMetrics{}, nil, metaServer)
    53  
    54  	checkpointManager, err := checkpointmanager.NewCheckpointManager("/tmp/GetTopologyAwareResources")
    55  	assert.NoError(t, err)
    56  
    57  	for _, tc := range []struct {
    58  		name         string
    59  		pod          *v1.Pod
    60  		container    *v1.Container
    61  		expectedResp []*v12.TopologyAwareResource
    62  	}{
    63  		{
    64  			name: "test1",
    65  			pod: &v1.Pod{
    66  				ObjectMeta: metav1.ObjectMeta{
    67  					Name:      "pod1",
    68  					Namespace: "namespace1",
    69  					UID:       "uid1",
    70  				},
    71  				Spec: v1.PodSpec{
    72  					Containers: []v1.Container{
    73  						{
    74  							Name: "container1",
    75  							Resources: v1.ResourceRequirements{
    76  								Requests: map[v1.ResourceName]resource.Quantity{
    77  									v1.ResourceCPU:    resource.MustParse("4"),
    78  									v1.ResourceMemory: resource.MustParse("8Gi"),
    79  								},
    80  							},
    81  						},
    82  					},
    83  				},
    84  			},
    85  			container: &v1.Container{
    86  				Name: "container1",
    87  				Resources: v1.ResourceRequirements{
    88  					Requests: map[v1.ResourceName]resource.Quantity{
    89  						v1.ResourceCPU:    resource.MustParse("4"),
    90  						v1.ResourceMemory: resource.MustParse("8Gi"),
    91  					},
    92  				},
    93  			},
    94  			expectedResp: []*v12.TopologyAwareResource{
    95  				{
    96  					ResourceName:               "cpu",
    97  					IsScalarResource:           true,
    98  					AggregatedQuantity:         4,
    99  					OriginalAggregatedQuantity: 4,
   100  					TopologyAwareQuantityList: []*v12.TopologyAwareQuantity{
   101  						{
   102  							ResourceValue: 4,
   103  							Node:          0,
   104  							TopologyLevel: v12.TopologyLevel_NUMA,
   105  						},
   106  					},
   107  					OriginalTopologyAwareQuantityList: []*v12.TopologyAwareQuantity{
   108  						{
   109  							ResourceValue: 4,
   110  							Node:          0,
   111  							TopologyLevel: v12.TopologyLevel_NUMA,
   112  						},
   113  					},
   114  				},
   115  				{
   116  					ResourceName:               "memory",
   117  					IsScalarResource:           true,
   118  					AggregatedQuantity:         8589934592,
   119  					OriginalAggregatedQuantity: 8589934592,
   120  					TopologyAwareQuantityList: []*v12.TopologyAwareQuantity{
   121  						{
   122  							ResourceValue: 8589934592,
   123  							Node:          0,
   124  							TopologyLevel: v12.TopologyLevel_NUMA,
   125  						},
   126  					},
   127  					OriginalTopologyAwareQuantityList: []*v12.TopologyAwareQuantity{
   128  						{
   129  							ResourceValue: 8589934592,
   130  							Node:          0,
   131  							TopologyLevel: v12.TopologyLevel_NUMA,
   132  						},
   133  					},
   134  				},
   135  			},
   136  		},
   137  		{
   138  			name:         "nil pod",
   139  			pod:          nil,
   140  			container:    nil,
   141  			expectedResp: []*v12.TopologyAwareResource{},
   142  		},
   143  		{
   144  			name: "skip pod",
   145  			pod: &v1.Pod{
   146  				ObjectMeta: metav1.ObjectMeta{
   147  					Name:      "pod",
   148  					Namespace: "namespace",
   149  					UID:       "uid",
   150  					OwnerReferences: []metav1.OwnerReference{
   151  						{
   152  							Kind: "DaemonSet",
   153  						},
   154  					},
   155  					Annotations: map[string]string{
   156  						"katalyst.kubewharf.io/qos_level": "shared_cores",
   157  					},
   158  				},
   159  			},
   160  			container: &v1.Container{
   161  				Name: "container",
   162  			},
   163  			expectedResp: nil,
   164  		},
   165  	} {
   166  		tc := tc
   167  		t.Run(tc.name, func(t *testing.T) {
   168  			t.Parallel()
   169  
   170  			m := &ManagerImpl{
   171  				reconcilePeriod:   time.Minute,
   172  				endpoints:         map[string]endpoint.EndpointInfo{},
   173  				socketdir:         "/tmp/GetTopologyAwareResources",
   174  				socketname:        "tmp.sock",
   175  				checkpointManager: checkpointManager,
   176  				qosConfig:         generic.NewQoSConfiguration(),
   177  				metaManager:       metamanager,
   178  				emitter:           metrics.DummyMetrics{},
   179  			}
   180  
   181  			m.registerEndpoint("cpu", &pluginapi.ResourcePluginOptions{
   182  				PreStartRequired:      true,
   183  				WithTopologyAlignment: true,
   184  				NeedReconcile:         true,
   185  			}, &MockEndpoint{
   186  				getTopologyAwareResourcesFunc: func(c context.Context, request *pluginapi.GetTopologyAwareResourcesRequest) (*pluginapi.GetTopologyAwareResourcesResponse, error) {
   187  					return &pluginapi.GetTopologyAwareResourcesResponse{
   188  						PodUid:       string(tc.pod.UID),
   189  						PodName:      tc.pod.Name,
   190  						PodNamespace: tc.pod.Namespace,
   191  						ContainerTopologyAwareResources: &pluginapi.ContainerTopologyAwareResources{
   192  							ContainerName:      tc.container.Name,
   193  							AllocatedResources: containerToCPUAllocatedResources(tc.container),
   194  						},
   195  					}, nil
   196  				},
   197  			})
   198  
   199  			m.registerEndpoint("memory", &pluginapi.ResourcePluginOptions{
   200  				PreStartRequired:      true,
   201  				WithTopologyAlignment: true,
   202  				NeedReconcile:         true,
   203  			}, &MockEndpoint{
   204  				getTopologyAwareResourcesFunc: func(c context.Context, request *pluginapi.GetTopologyAwareResourcesRequest) (*pluginapi.GetTopologyAwareResourcesResponse, error) {
   205  					return &pluginapi.GetTopologyAwareResourcesResponse{
   206  						PodUid:       string(tc.pod.UID),
   207  						PodName:      tc.pod.Name,
   208  						PodNamespace: tc.pod.Namespace,
   209  						ContainerTopologyAwareResources: &pluginapi.ContainerTopologyAwareResources{
   210  							ContainerName:      tc.container.Name,
   211  							AllocatedResources: containerToMemoryAllocatedResources(tc.container),
   212  						},
   213  					}, nil
   214  				},
   215  			})
   216  
   217  			resp := m.GetTopologyAwareResources(tc.pod, tc.container)
   218  			assert.True(t, equalTopologyAwareResourceList(resp, tc.expectedResp))
   219  		})
   220  	}
   221  }
   222  
   223  func TestGetTopologyAwareAllocatableResources(t *testing.T) {
   224  	t.Parallel()
   225  
   226  	ckDir, err := ioutil.TempDir("", "checkpoint-Test")
   227  	assert.NoError(t, err)
   228  	defer func() { _ = os.RemoveAll(ckDir) }()
   229  
   230  	conf := generateTestConfiguration(ckDir)
   231  	metaServer, err := generateTestMetaServer(conf, []*v1.Pod{})
   232  	assert.NoError(t, err)
   233  	metamanager := metamanager.NewManager(metrics.DummyMetrics{}, nil, metaServer)
   234  
   235  	checkpointManager, err := checkpointmanager.NewCheckpointManager("/tmp/GetTopologyAwareAllocatableResources")
   236  	assert.NoError(t, err)
   237  
   238  	m := &ManagerImpl{
   239  		reconcilePeriod:   time.Minute,
   240  		endpoints:         map[string]endpoint.EndpointInfo{},
   241  		socketdir:         "/tmp/GetTopologyAwareAllocatableResources",
   242  		socketname:        "tmp.sock",
   243  		checkpointManager: checkpointManager,
   244  		qosConfig:         generic.NewQoSConfiguration(),
   245  		metaManager:       metamanager,
   246  		emitter:           metrics.DummyMetrics{},
   247  	}
   248  
   249  	for _, tc := range []struct {
   250  		name         string
   251  		cpu          float64
   252  		memory       float64
   253  		expectedResp []*v12.AllocatableTopologyAwareResource
   254  	}{
   255  		{
   256  			name:   "test1",
   257  			cpu:    4,
   258  			memory: 8589934592,
   259  			expectedResp: []*v12.AllocatableTopologyAwareResource{
   260  				{
   261  					ResourceName:                  "cpu",
   262  					IsScalarResource:              true,
   263  					AggregatedAllocatableQuantity: 4,
   264  					AggregatedCapacityQuantity:    4,
   265  					TopologyAwareAllocatableQuantityList: []*v12.TopologyAwareQuantity{
   266  						{
   267  							ResourceValue: 4,
   268  							Node:          0,
   269  							TopologyLevel: v12.TopologyLevel_NUMA,
   270  						},
   271  					},
   272  					TopologyAwareCapacityQuantityList: []*v12.TopologyAwareQuantity{
   273  						{
   274  							ResourceValue: 4,
   275  							Node:          0,
   276  							TopologyLevel: v12.TopologyLevel_NUMA,
   277  						},
   278  					},
   279  				},
   280  				{
   281  					ResourceName:                  "memory",
   282  					IsScalarResource:              true,
   283  					AggregatedAllocatableQuantity: 8589934592,
   284  					AggregatedCapacityQuantity:    8589934592,
   285  					TopologyAwareAllocatableQuantityList: []*v12.TopologyAwareQuantity{
   286  						{
   287  							ResourceValue: 8589934592,
   288  							Node:          0,
   289  							TopologyLevel: v12.TopologyLevel_NUMA,
   290  						},
   291  					},
   292  					TopologyAwareCapacityQuantityList: []*v12.TopologyAwareQuantity{
   293  						{
   294  							ResourceValue: 8589934592,
   295  							Node:          0,
   296  							TopologyLevel: v12.TopologyLevel_NUMA,
   297  						},
   298  					},
   299  				},
   300  			},
   301  		},
   302  	} {
   303  		tc := tc
   304  		t.Run(tc.name, func(t *testing.T) {
   305  			t.Parallel()
   306  			m.registerEndpoint("cpu", &pluginapi.ResourcePluginOptions{
   307  				PreStartRequired:      true,
   308  				WithTopologyAlignment: true,
   309  				NeedReconcile:         true,
   310  			}, &MockEndpoint{
   311  				getTopologyAwareAllocatableResourcesFunc: func(c context.Context, request *pluginapi.GetTopologyAwareAllocatableResourcesRequest) (*pluginapi.GetTopologyAwareAllocatableResourcesResponse, error) {
   312  					return &pluginapi.GetTopologyAwareAllocatableResourcesResponse{
   313  						AllocatableResources: map[string]*pluginapi.AllocatableTopologyAwareResource{
   314  							"cpu": {
   315  								IsScalarResource:              true,
   316  								AggregatedCapacityQuantity:    tc.cpu,
   317  								AggregatedAllocatableQuantity: tc.cpu,
   318  								TopologyAwareAllocatableQuantityList: []*pluginapi.TopologyAwareQuantity{
   319  									{
   320  										ResourceValue: tc.cpu,
   321  										Node:          0,
   322  										TopologyLevel: pluginapi.TopologyLevel_NUMA,
   323  									},
   324  								},
   325  								TopologyAwareCapacityQuantityList: []*pluginapi.TopologyAwareQuantity{
   326  									{
   327  										ResourceValue: tc.cpu,
   328  										Node:          0,
   329  										TopologyLevel: pluginapi.TopologyLevel_NUMA,
   330  									},
   331  								},
   332  							},
   333  						},
   334  					}, nil
   335  				},
   336  			})
   337  
   338  			m.registerEndpoint("memory", &pluginapi.ResourcePluginOptions{
   339  				PreStartRequired:      true,
   340  				WithTopologyAlignment: true,
   341  				NeedReconcile:         true,
   342  			}, &MockEndpoint{
   343  				getTopologyAwareAllocatableResourcesFunc: func(c context.Context, request *pluginapi.GetTopologyAwareAllocatableResourcesRequest) (*pluginapi.GetTopologyAwareAllocatableResourcesResponse, error) {
   344  					return &pluginapi.GetTopologyAwareAllocatableResourcesResponse{
   345  						AllocatableResources: map[string]*pluginapi.AllocatableTopologyAwareResource{
   346  							"memory": {
   347  								IsScalarResource:              true,
   348  								AggregatedCapacityQuantity:    tc.memory,
   349  								AggregatedAllocatableQuantity: tc.memory,
   350  								TopologyAwareAllocatableQuantityList: []*pluginapi.TopologyAwareQuantity{
   351  									{
   352  										ResourceValue: tc.memory,
   353  										Node:          0,
   354  										TopologyLevel: pluginapi.TopologyLevel_NUMA,
   355  									},
   356  								},
   357  								TopologyAwareCapacityQuantityList: []*pluginapi.TopologyAwareQuantity{
   358  									{
   359  										ResourceValue: tc.memory,
   360  										Node:          0,
   361  										TopologyLevel: pluginapi.TopologyLevel_NUMA,
   362  									},
   363  								},
   364  							},
   365  						},
   366  					}, nil
   367  				},
   368  			})
   369  
   370  			resp := m.GetTopologyAwareAllocatableResources()
   371  			assert.True(t, equalAllocatableTopologyAwareResourceList(resp, tc.expectedResp))
   372  		})
   373  	}
   374  }
   375  
   376  func containerToCPUAllocatedResources(container *v1.Container) map[string]*pluginapi.TopologyAwareResource {
   377  	res := map[string]*pluginapi.TopologyAwareResource{}
   378  
   379  	if container == nil {
   380  		return res
   381  	}
   382  	res["cpu"] = &pluginapi.TopologyAwareResource{
   383  		IsScalarResource:           true,
   384  		AggregatedQuantity:         float64(container.Resources.Requests.Cpu().Value()),
   385  		OriginalAggregatedQuantity: float64(container.Resources.Requests.Cpu().Value()),
   386  		TopologyAwareQuantityList: []*pluginapi.TopologyAwareQuantity{
   387  			{
   388  				ResourceValue: float64(container.Resources.Requests.Cpu().Value()),
   389  				Node:          0,
   390  				TopologyLevel: pluginapi.TopologyLevel_NUMA,
   391  			},
   392  		},
   393  		OriginalTopologyAwareQuantityList: []*pluginapi.TopologyAwareQuantity{
   394  			{
   395  				ResourceValue: float64(container.Resources.Requests.Cpu().Value()),
   396  				Node:          0,
   397  				TopologyLevel: pluginapi.TopologyLevel_NUMA,
   398  			},
   399  		},
   400  	}
   401  
   402  	return res
   403  }
   404  
   405  func containerToMemoryAllocatedResources(container *v1.Container) map[string]*pluginapi.TopologyAwareResource {
   406  	res := map[string]*pluginapi.TopologyAwareResource{}
   407  
   408  	res["memory"] = &pluginapi.TopologyAwareResource{
   409  		IsScalarResource:           true,
   410  		AggregatedQuantity:         float64(container.Resources.Requests.Memory().Value()),
   411  		OriginalAggregatedQuantity: float64(container.Resources.Requests.Memory().Value()),
   412  		TopologyAwareQuantityList: []*pluginapi.TopologyAwareQuantity{
   413  			{
   414  				ResourceValue: float64(container.Resources.Requests.Memory().Value()),
   415  				Node:          0,
   416  				TopologyLevel: pluginapi.TopologyLevel_NUMA,
   417  			},
   418  		},
   419  		OriginalTopologyAwareQuantityList: []*pluginapi.TopologyAwareQuantity{
   420  			{
   421  				ResourceValue: float64(container.Resources.Requests.Memory().Value()),
   422  				Node:          0,
   423  				TopologyLevel: pluginapi.TopologyLevel_NUMA,
   424  			},
   425  		},
   426  	}
   427  
   428  	return res
   429  }
   430  
   431  func equalTopologyAwareResourceList(a, b []*v12.TopologyAwareResource) bool {
   432  	if len(a) != len(b) {
   433  		return false
   434  	}
   435  
   436  	mapa := topologyAwareResourceListToMap(a)
   437  	mapb := topologyAwareResourceListToMap(b)
   438  
   439  	for resourceName, resourcea := range mapa {
   440  		resourceb, ok := mapb[resourceName]
   441  		if !ok {
   442  			return false
   443  		}
   444  
   445  		if resourcea.IsScalarResource != resourceb.IsScalarResource ||
   446  			resourcea.IsNodeResource != resourceb.IsNodeResource ||
   447  			resourcea.AggregatedQuantity != resourceb.AggregatedQuantity ||
   448  			resourcea.OriginalAggregatedQuantity != resourceb.OriginalAggregatedQuantity {
   449  			return false
   450  		}
   451  
   452  		if !reflect.DeepEqual(resourcea.TopologyAwareQuantityList, resourceb.TopologyAwareQuantityList) {
   453  			return false
   454  		}
   455  		if !reflect.DeepEqual(resourcea.OriginalTopologyAwareQuantityList, resourceb.OriginalTopologyAwareQuantityList) {
   456  			return false
   457  		}
   458  	}
   459  
   460  	return true
   461  }
   462  
   463  func equalAllocatableTopologyAwareResourceList(a, b []*v12.AllocatableTopologyAwareResource) bool {
   464  	if len(a) != len(b) {
   465  		return false
   466  	}
   467  
   468  	mapa := allocatableTopologyAwareResourceListToMap(a)
   469  	mapb := allocatableTopologyAwareResourceListToMap(b)
   470  
   471  	for resourceName, resourcea := range mapa {
   472  		resourceb, ok := mapb[resourceName]
   473  		if !ok {
   474  			return false
   475  		}
   476  
   477  		if resourcea.IsScalarResource != resourceb.IsScalarResource ||
   478  			resourcea.IsNodeResource != resourceb.IsNodeResource ||
   479  			resourcea.AggregatedAllocatableQuantity != resourceb.AggregatedAllocatableQuantity ||
   480  			resourcea.AggregatedCapacityQuantity != resourceb.AggregatedCapacityQuantity {
   481  			return false
   482  		}
   483  
   484  		if !reflect.DeepEqual(resourcea.TopologyAwareAllocatableQuantityList, resourceb.TopologyAwareAllocatableQuantityList) {
   485  			return false
   486  		}
   487  		if !reflect.DeepEqual(resourcea.TopologyAwareCapacityQuantityList, resourceb.TopologyAwareCapacityQuantityList) {
   488  			return false
   489  		}
   490  	}
   491  
   492  	return true
   493  }
   494  
   495  func topologyAwareResourceListToMap(datas []*v12.TopologyAwareResource) map[string]*v12.TopologyAwareResource {
   496  	res := map[string]*v12.TopologyAwareResource{}
   497  	for _, data := range datas {
   498  		res[data.ResourceName] = data
   499  	}
   500  	return res
   501  }
   502  
   503  func allocatableTopologyAwareResourceListToMap(datas []*v12.AllocatableTopologyAwareResource) map[string]*v12.AllocatableTopologyAwareResource {
   504  	res := map[string]*v12.AllocatableTopologyAwareResource{}
   505  	for _, data := range datas {
   506  		res[data.ResourceName] = data
   507  	}
   508  	return res
   509  }