k8s.io/kubernetes@v1.29.3/pkg/kubelet/apis/podresources/server_v1_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes 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 podresources
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"sort"
    24  	"testing"
    25  
    26  	"github.com/golang/mock/gomock"
    27  	v1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    31  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    32  	podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
    33  	pkgfeatures "k8s.io/kubernetes/pkg/features"
    34  	podresourcetest "k8s.io/kubernetes/pkg/kubelet/apis/podresources/testing"
    35  )
    36  
    37  func TestListPodResourcesV1(t *testing.T) {
    38  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true)()
    39  
    40  	podName := "pod-name"
    41  	podNamespace := "pod-namespace"
    42  	podUID := types.UID("pod-uid")
    43  	containerName := "container-name"
    44  	numaID := int64(1)
    45  
    46  	mockCtrl := gomock.NewController(t)
    47  	defer mockCtrl.Finish()
    48  
    49  	devs := []*podresourcesapi.ContainerDevices{
    50  		{
    51  			ResourceName: "resource",
    52  			DeviceIds:    []string{"dev0", "dev1"},
    53  			Topology:     &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
    54  		},
    55  	}
    56  
    57  	cpus := []int64{12, 23, 30}
    58  
    59  	memory := []*podresourcesapi.ContainerMemory{
    60  		{
    61  			MemoryType: "memory",
    62  			Size_:      1073741824,
    63  			Topology:   &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
    64  		},
    65  		{
    66  			MemoryType: "hugepages-1Gi",
    67  			Size_:      1073741824,
    68  			Topology:   &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
    69  		},
    70  	}
    71  
    72  	containers := []v1.Container{
    73  		{
    74  			Name: containerName,
    75  		},
    76  	}
    77  	pods := []*v1.Pod{
    78  		{
    79  			ObjectMeta: metav1.ObjectMeta{
    80  				Name:      podName,
    81  				Namespace: podNamespace,
    82  				UID:       podUID,
    83  			},
    84  			Spec: v1.PodSpec{
    85  				Containers: containers,
    86  			},
    87  		},
    88  	}
    89  
    90  	pluginCDIDevices := []*podresourcesapi.CDIDevice{{Name: "dra-dev0"}, {Name: "dra-dev1"}}
    91  	draDevs := []*podresourcesapi.DynamicResource{
    92  		{
    93  			ClassName:      "resource-class",
    94  			ClaimName:      "claim-name",
    95  			ClaimNamespace: "default",
    96  			ClaimResources: []*podresourcesapi.ClaimResource{{CDIDevices: pluginCDIDevices}},
    97  		},
    98  	}
    99  
   100  	for _, tc := range []struct {
   101  		desc             string
   102  		pods             []*v1.Pod
   103  		devices          []*podresourcesapi.ContainerDevices
   104  		cpus             []int64
   105  		memory           []*podresourcesapi.ContainerMemory
   106  		dynamicResources []*podresourcesapi.DynamicResource
   107  		expectedResponse *podresourcesapi.ListPodResourcesResponse
   108  	}{
   109  		{
   110  			desc:             "no pods",
   111  			pods:             []*v1.Pod{},
   112  			devices:          []*podresourcesapi.ContainerDevices{},
   113  			cpus:             []int64{},
   114  			memory:           []*podresourcesapi.ContainerMemory{},
   115  			dynamicResources: []*podresourcesapi.DynamicResource{},
   116  			expectedResponse: &podresourcesapi.ListPodResourcesResponse{},
   117  		},
   118  		{
   119  			desc:             "pod without devices",
   120  			pods:             pods,
   121  			devices:          []*podresourcesapi.ContainerDevices{},
   122  			cpus:             []int64{},
   123  			memory:           []*podresourcesapi.ContainerMemory{},
   124  			dynamicResources: []*podresourcesapi.DynamicResource{},
   125  			expectedResponse: &podresourcesapi.ListPodResourcesResponse{
   126  				PodResources: []*podresourcesapi.PodResources{
   127  					{
   128  						Name:      podName,
   129  						Namespace: podNamespace,
   130  						Containers: []*podresourcesapi.ContainerResources{
   131  							{
   132  								Name:             containerName,
   133  								Devices:          []*podresourcesapi.ContainerDevices{},
   134  								DynamicResources: []*podresourcesapi.DynamicResource{},
   135  							},
   136  						},
   137  					},
   138  				},
   139  			},
   140  		},
   141  		{
   142  			desc:             "pod with devices",
   143  			pods:             pods,
   144  			devices:          devs,
   145  			cpus:             cpus,
   146  			memory:           memory,
   147  			dynamicResources: []*podresourcesapi.DynamicResource{},
   148  			expectedResponse: &podresourcesapi.ListPodResourcesResponse{
   149  				PodResources: []*podresourcesapi.PodResources{
   150  					{
   151  						Name:      podName,
   152  						Namespace: podNamespace,
   153  						Containers: []*podresourcesapi.ContainerResources{
   154  							{
   155  								Name:             containerName,
   156  								Devices:          devs,
   157  								CpuIds:           cpus,
   158  								Memory:           memory,
   159  								DynamicResources: []*podresourcesapi.DynamicResource{},
   160  							},
   161  						},
   162  					},
   163  				},
   164  			},
   165  		},
   166  		{
   167  			desc:             "pod with dynamic resources",
   168  			pods:             pods,
   169  			devices:          []*podresourcesapi.ContainerDevices{},
   170  			cpus:             cpus,
   171  			memory:           memory,
   172  			dynamicResources: draDevs,
   173  			expectedResponse: &podresourcesapi.ListPodResourcesResponse{
   174  				PodResources: []*podresourcesapi.PodResources{
   175  					{
   176  						Name:      podName,
   177  						Namespace: podNamespace,
   178  						Containers: []*podresourcesapi.ContainerResources{
   179  							{
   180  								Name:             containerName,
   181  								Devices:          []*podresourcesapi.ContainerDevices{},
   182  								CpuIds:           cpus,
   183  								Memory:           memory,
   184  								DynamicResources: draDevs,
   185  							},
   186  						},
   187  					},
   188  				},
   189  			},
   190  		},
   191  		{
   192  			desc:             "pod with dynamic resources and devices",
   193  			pods:             pods,
   194  			devices:          devs,
   195  			cpus:             cpus,
   196  			memory:           memory,
   197  			dynamicResources: draDevs,
   198  			expectedResponse: &podresourcesapi.ListPodResourcesResponse{
   199  				PodResources: []*podresourcesapi.PodResources{
   200  					{
   201  						Name:      podName,
   202  						Namespace: podNamespace,
   203  						Containers: []*podresourcesapi.ContainerResources{
   204  							{
   205  								Name:             containerName,
   206  								Devices:          devs,
   207  								CpuIds:           cpus,
   208  								Memory:           memory,
   209  								DynamicResources: draDevs,
   210  							},
   211  						},
   212  					},
   213  				},
   214  			},
   215  		},
   216  	} {
   217  		t.Run(tc.desc, func(t *testing.T) {
   218  			mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl)
   219  			mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl)
   220  			mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl)
   221  			mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl)
   222  			mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl)
   223  
   224  			mockPodsProvider.EXPECT().GetPods().Return(tc.pods).AnyTimes().AnyTimes()
   225  			mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes()
   226  			mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes()
   227  			mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes()
   228  			mockDynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &containers[0]).Return(tc.dynamicResources).AnyTimes()
   229  			mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes()
   230  			mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes()
   231  			mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes()
   232  			mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes()
   233  
   234  			providers := PodResourcesProviders{
   235  				Pods:             mockPodsProvider,
   236  				Devices:          mockDevicesProvider,
   237  				Cpus:             mockCPUsProvider,
   238  				Memory:           mockMemoryProvider,
   239  				DynamicResources: mockDynamicResourcesProvider,
   240  			}
   241  			server := NewV1PodResourcesServer(providers)
   242  			resp, err := server.List(context.TODO(), &podresourcesapi.ListPodResourcesRequest{})
   243  			if err != nil {
   244  				t.Errorf("want err = %v, got %q", nil, err)
   245  			}
   246  			if !equalListResponse(tc.expectedResponse, resp) {
   247  				t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String())
   248  			}
   249  		})
   250  	}
   251  }
   252  
   253  func TestAllocatableResources(t *testing.T) {
   254  	mockCtrl := gomock.NewController(t)
   255  	defer mockCtrl.Finish()
   256  
   257  	allDevs := []*podresourcesapi.ContainerDevices{
   258  		{
   259  			ResourceName: "resource",
   260  			DeviceIds:    []string{"dev0"},
   261  			Topology: &podresourcesapi.TopologyInfo{
   262  				Nodes: []*podresourcesapi.NUMANode{
   263  					{
   264  						ID: 0,
   265  					},
   266  				},
   267  			},
   268  		},
   269  		{
   270  			ResourceName: "resource",
   271  			DeviceIds:    []string{"dev1"},
   272  			Topology: &podresourcesapi.TopologyInfo{
   273  				Nodes: []*podresourcesapi.NUMANode{
   274  					{
   275  						ID: 1,
   276  					},
   277  				},
   278  			},
   279  		},
   280  		{
   281  			ResourceName: "resource-nt",
   282  			DeviceIds:    []string{"devA"},
   283  		},
   284  		{
   285  			ResourceName: "resource-mm",
   286  			DeviceIds:    []string{"devM0"},
   287  			Topology: &podresourcesapi.TopologyInfo{
   288  				Nodes: []*podresourcesapi.NUMANode{
   289  					{
   290  						ID: 0,
   291  					},
   292  				},
   293  			},
   294  		},
   295  		{
   296  			ResourceName: "resource-mm",
   297  			DeviceIds:    []string{"devMM"},
   298  			Topology: &podresourcesapi.TopologyInfo{
   299  				Nodes: []*podresourcesapi.NUMANode{
   300  					{
   301  						ID: 0,
   302  					},
   303  					{
   304  						ID: 1,
   305  					},
   306  				},
   307  			},
   308  		},
   309  	}
   310  
   311  	allCPUs := []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   312  
   313  	allMemory := []*podresourcesapi.ContainerMemory{
   314  		{
   315  			MemoryType: "memory",
   316  			Size_:      5368709120,
   317  			Topology: &podresourcesapi.TopologyInfo{
   318  				Nodes: []*podresourcesapi.NUMANode{
   319  					{
   320  						ID: 0,
   321  					},
   322  				},
   323  			},
   324  		},
   325  		{
   326  			MemoryType: "hugepages-2Mi",
   327  			Size_:      1073741824,
   328  			Topology: &podresourcesapi.TopologyInfo{
   329  				Nodes: []*podresourcesapi.NUMANode{
   330  					{
   331  						ID: 0,
   332  					},
   333  				},
   334  			},
   335  		},
   336  		{
   337  			MemoryType: "memory",
   338  			Size_:      5368709120,
   339  			Topology: &podresourcesapi.TopologyInfo{
   340  				Nodes: []*podresourcesapi.NUMANode{
   341  					{
   342  						ID: 1,
   343  					},
   344  				},
   345  			},
   346  		},
   347  		{
   348  			MemoryType: "hugepages-2Mi",
   349  			Size_:      1073741824,
   350  			Topology: &podresourcesapi.TopologyInfo{
   351  				Nodes: []*podresourcesapi.NUMANode{
   352  					{
   353  						ID: 1,
   354  					},
   355  				},
   356  			},
   357  		},
   358  	}
   359  
   360  	for _, tc := range []struct {
   361  		desc                                 string
   362  		allCPUs                              []int64
   363  		allDevices                           []*podresourcesapi.ContainerDevices
   364  		allMemory                            []*podresourcesapi.ContainerMemory
   365  		expectedAllocatableResourcesResponse *podresourcesapi.AllocatableResourcesResponse
   366  	}{
   367  		{
   368  			desc:                                 "no devices, no CPUs",
   369  			allCPUs:                              []int64{},
   370  			allDevices:                           []*podresourcesapi.ContainerDevices{},
   371  			allMemory:                            []*podresourcesapi.ContainerMemory{},
   372  			expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{},
   373  		},
   374  		{
   375  			desc:       "no devices, all CPUs",
   376  			allCPUs:    allCPUs,
   377  			allDevices: []*podresourcesapi.ContainerDevices{},
   378  			expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{
   379  				CpuIds: allCPUs,
   380  			},
   381  		},
   382  		{
   383  			desc:       "no devices, no CPUs, all memory",
   384  			allCPUs:    []int64{},
   385  			allDevices: []*podresourcesapi.ContainerDevices{},
   386  			expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{
   387  				Memory: allMemory,
   388  			},
   389  		},
   390  		{
   391  			desc:       "with devices, all CPUs",
   392  			allCPUs:    allCPUs,
   393  			allDevices: allDevs,
   394  			expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{
   395  				CpuIds: allCPUs,
   396  				Devices: []*podresourcesapi.ContainerDevices{
   397  					{
   398  						ResourceName: "resource",
   399  						DeviceIds:    []string{"dev0"},
   400  						Topology: &podresourcesapi.TopologyInfo{
   401  							Nodes: []*podresourcesapi.NUMANode{
   402  								{
   403  									ID: 0,
   404  								},
   405  							},
   406  						},
   407  					},
   408  					{
   409  						ResourceName: "resource",
   410  						DeviceIds:    []string{"dev1"},
   411  						Topology: &podresourcesapi.TopologyInfo{
   412  							Nodes: []*podresourcesapi.NUMANode{
   413  								{
   414  									ID: 1,
   415  								},
   416  							},
   417  						},
   418  					},
   419  					{
   420  						ResourceName: "resource-nt",
   421  						DeviceIds:    []string{"devA"},
   422  					},
   423  					{
   424  						ResourceName: "resource-mm",
   425  						DeviceIds:    []string{"devM0"},
   426  						Topology: &podresourcesapi.TopologyInfo{
   427  							Nodes: []*podresourcesapi.NUMANode{
   428  								{
   429  									ID: 0,
   430  								},
   431  							},
   432  						},
   433  					},
   434  					{
   435  						ResourceName: "resource-mm",
   436  						DeviceIds:    []string{"devMM"},
   437  						Topology: &podresourcesapi.TopologyInfo{
   438  							Nodes: []*podresourcesapi.NUMANode{
   439  								{
   440  									ID: 0,
   441  								},
   442  								{
   443  									ID: 1,
   444  								},
   445  							},
   446  						},
   447  					},
   448  				},
   449  			},
   450  		},
   451  		{
   452  			desc:       "with devices, no CPUs",
   453  			allCPUs:    []int64{},
   454  			allDevices: allDevs,
   455  			expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{
   456  				Devices: []*podresourcesapi.ContainerDevices{
   457  					{
   458  						ResourceName: "resource",
   459  						DeviceIds:    []string{"dev0"},
   460  						Topology: &podresourcesapi.TopologyInfo{
   461  							Nodes: []*podresourcesapi.NUMANode{
   462  								{
   463  									ID: 0,
   464  								},
   465  							},
   466  						},
   467  					},
   468  					{
   469  						ResourceName: "resource",
   470  						DeviceIds:    []string{"dev1"},
   471  						Topology: &podresourcesapi.TopologyInfo{
   472  							Nodes: []*podresourcesapi.NUMANode{
   473  								{
   474  									ID: 1,
   475  								},
   476  							},
   477  						},
   478  					},
   479  					{
   480  						ResourceName: "resource-nt",
   481  						DeviceIds:    []string{"devA"},
   482  					},
   483  					{
   484  						ResourceName: "resource-mm",
   485  						DeviceIds:    []string{"devM0"},
   486  						Topology: &podresourcesapi.TopologyInfo{
   487  							Nodes: []*podresourcesapi.NUMANode{
   488  								{
   489  									ID: 0,
   490  								},
   491  							},
   492  						},
   493  					},
   494  					{
   495  						ResourceName: "resource-mm",
   496  						DeviceIds:    []string{"devMM"},
   497  						Topology: &podresourcesapi.TopologyInfo{
   498  							Nodes: []*podresourcesapi.NUMANode{
   499  								{
   500  									ID: 0,
   501  								},
   502  								{
   503  									ID: 1,
   504  								},
   505  							},
   506  						},
   507  					},
   508  				},
   509  			},
   510  		},
   511  	} {
   512  		t.Run(tc.desc, func(t *testing.T) {
   513  			mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl)
   514  			mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl)
   515  			mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl)
   516  			mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl)
   517  
   518  			mockDevicesProvider.EXPECT().GetDevices("", "").Return([]*podresourcesapi.ContainerDevices{}).AnyTimes()
   519  			mockCPUsProvider.EXPECT().GetCPUs("", "").Return([]int64{}).AnyTimes()
   520  			mockMemoryProvider.EXPECT().GetMemory("", "").Return([]*podresourcesapi.ContainerMemory{}).AnyTimes()
   521  			mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes()
   522  			mockDevicesProvider.EXPECT().GetAllocatableDevices().Return(tc.allDevices).AnyTimes()
   523  			mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return(tc.allCPUs).AnyTimes()
   524  			mockMemoryProvider.EXPECT().GetAllocatableMemory().Return(tc.allMemory).AnyTimes()
   525  
   526  			providers := PodResourcesProviders{
   527  				Pods:    mockPodsProvider,
   528  				Devices: mockDevicesProvider,
   529  				Cpus:    mockCPUsProvider,
   530  				Memory:  mockMemoryProvider,
   531  			}
   532  			server := NewV1PodResourcesServer(providers)
   533  
   534  			resp, err := server.GetAllocatableResources(context.TODO(), &podresourcesapi.AllocatableResourcesRequest{})
   535  			if err != nil {
   536  				t.Errorf("want err = %v, got %q", nil, err)
   537  			}
   538  
   539  			if !equalAllocatableResourcesResponse(tc.expectedAllocatableResourcesResponse, resp) {
   540  				t.Errorf("want resp = %s, got %s", tc.expectedAllocatableResourcesResponse.String(), resp.String())
   541  			}
   542  		})
   543  	}
   544  }
   545  
   546  func TestGetPodResourcesV1(t *testing.T) {
   547  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesGet, true)()
   548  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true)()
   549  
   550  	podName := "pod-name"
   551  	podNamespace := "pod-namespace"
   552  	podUID := types.UID("pod-uid")
   553  	containerName := "container-name"
   554  	numaID := int64(1)
   555  
   556  	mockCtrl := gomock.NewController(t)
   557  	defer mockCtrl.Finish()
   558  
   559  	devs := []*podresourcesapi.ContainerDevices{
   560  		{
   561  			ResourceName: "resource",
   562  			DeviceIds:    []string{"dev0", "dev1"},
   563  			Topology:     &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
   564  		},
   565  	}
   566  
   567  	cpus := []int64{12, 23, 30}
   568  
   569  	memory := []*podresourcesapi.ContainerMemory{
   570  		{
   571  			MemoryType: "memory",
   572  			Size_:      1073741824,
   573  			Topology:   &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
   574  		},
   575  		{
   576  			MemoryType: "hugepages-1Gi",
   577  			Size_:      1073741824,
   578  			Topology:   &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
   579  		},
   580  	}
   581  
   582  	containers := []v1.Container{
   583  		{
   584  			Name: containerName,
   585  		},
   586  	}
   587  
   588  	pod := &v1.Pod{
   589  		ObjectMeta: metav1.ObjectMeta{
   590  			Name:      podName,
   591  			Namespace: podNamespace,
   592  			UID:       podUID,
   593  		},
   594  		Spec: v1.PodSpec{
   595  			Containers: containers,
   596  		},
   597  	}
   598  
   599  	pluginCDIDevices := []*podresourcesapi.CDIDevice{{Name: "dra-dev0"}, {Name: "dra-dev1"}}
   600  	draDevs := []*podresourcesapi.DynamicResource{
   601  		{
   602  			ClassName:      "resource-class",
   603  			ClaimName:      "claim-name",
   604  			ClaimNamespace: "default",
   605  			ClaimResources: []*podresourcesapi.ClaimResource{{CDIDevices: pluginCDIDevices}},
   606  		},
   607  	}
   608  
   609  	for _, tc := range []struct {
   610  		desc             string
   611  		err              error
   612  		exist            bool
   613  		pod              *v1.Pod
   614  		devices          []*podresourcesapi.ContainerDevices
   615  		cpus             []int64
   616  		memory           []*podresourcesapi.ContainerMemory
   617  		dynamicResources []*podresourcesapi.DynamicResource
   618  		expectedResponse *podresourcesapi.GetPodResourcesResponse
   619  	}{
   620  		{
   621  			desc:             "pod not exist",
   622  			err:              fmt.Errorf("pod %s in namespace %s not found", podName, podNamespace),
   623  			exist:            false,
   624  			pod:              nil,
   625  			devices:          []*podresourcesapi.ContainerDevices{},
   626  			cpus:             []int64{},
   627  			memory:           []*podresourcesapi.ContainerMemory{},
   628  			dynamicResources: []*podresourcesapi.DynamicResource{},
   629  
   630  			expectedResponse: &podresourcesapi.GetPodResourcesResponse{},
   631  		},
   632  		{
   633  			desc:             "pod without devices",
   634  			err:              nil,
   635  			exist:            true,
   636  			pod:              pod,
   637  			devices:          []*podresourcesapi.ContainerDevices{},
   638  			cpus:             []int64{},
   639  			memory:           []*podresourcesapi.ContainerMemory{},
   640  			dynamicResources: []*podresourcesapi.DynamicResource{},
   641  			expectedResponse: &podresourcesapi.GetPodResourcesResponse{
   642  				PodResources: &podresourcesapi.PodResources{
   643  					Name:      podName,
   644  					Namespace: podNamespace,
   645  					Containers: []*podresourcesapi.ContainerResources{
   646  						{
   647  							Name:             containerName,
   648  							Devices:          []*podresourcesapi.ContainerDevices{},
   649  							DynamicResources: []*podresourcesapi.DynamicResource{},
   650  						},
   651  					},
   652  				},
   653  			},
   654  		},
   655  		{
   656  			desc:             "pod with devices",
   657  			err:              nil,
   658  			exist:            true,
   659  			pod:              pod,
   660  			devices:          devs,
   661  			cpus:             cpus,
   662  			memory:           memory,
   663  			dynamicResources: draDevs,
   664  			expectedResponse: &podresourcesapi.GetPodResourcesResponse{
   665  				PodResources: &podresourcesapi.PodResources{
   666  					Name:      podName,
   667  					Namespace: podNamespace,
   668  					Containers: []*podresourcesapi.ContainerResources{
   669  						{
   670  							Name:             containerName,
   671  							Devices:          devs,
   672  							CpuIds:           cpus,
   673  							Memory:           memory,
   674  							DynamicResources: draDevs,
   675  						},
   676  					},
   677  				},
   678  			},
   679  		},
   680  	} {
   681  		t.Run(tc.desc, func(t *testing.T) {
   682  			mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl)
   683  			mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl)
   684  			mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl)
   685  			mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl)
   686  			mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl)
   687  
   688  			mockPodsProvider.EXPECT().GetPodByName(podNamespace, podName).Return(tc.pod, tc.exist).AnyTimes()
   689  			mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes()
   690  			mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes()
   691  			mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes()
   692  			mockDynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &containers[0]).Return(tc.dynamicResources).AnyTimes()
   693  			mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes()
   694  			mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes()
   695  			mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes()
   696  			mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes()
   697  
   698  			providers := PodResourcesProviders{
   699  				Pods:             mockPodsProvider,
   700  				Devices:          mockDevicesProvider,
   701  				Cpus:             mockCPUsProvider,
   702  				Memory:           mockMemoryProvider,
   703  				DynamicResources: mockDynamicResourcesProvider,
   704  			}
   705  			server := NewV1PodResourcesServer(providers)
   706  			podReq := &podresourcesapi.GetPodResourcesRequest{PodName: podName, PodNamespace: podNamespace}
   707  			resp, err := server.Get(context.TODO(), podReq)
   708  
   709  			if err != nil {
   710  				if err.Error() != tc.err.Error() {
   711  					t.Errorf("want exit = %v, got %v", tc.err, err)
   712  				}
   713  			} else {
   714  				if err != tc.err {
   715  					t.Errorf("want exit = %v, got %v", tc.err, err)
   716  				} else {
   717  					if !equalGetResponse(tc.expectedResponse, resp) {
   718  						t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String())
   719  					}
   720  				}
   721  			}
   722  		})
   723  	}
   724  
   725  }
   726  
   727  func equalListResponse(respA, respB *podresourcesapi.ListPodResourcesResponse) bool {
   728  	if len(respA.PodResources) != len(respB.PodResources) {
   729  		return false
   730  	}
   731  	for idx := 0; idx < len(respA.PodResources); idx++ {
   732  		podResA := respA.PodResources[idx]
   733  		podResB := respB.PodResources[idx]
   734  		if podResA.Name != podResB.Name {
   735  			return false
   736  		}
   737  		if podResA.Namespace != podResB.Namespace {
   738  			return false
   739  		}
   740  		if len(podResA.Containers) != len(podResB.Containers) {
   741  			return false
   742  		}
   743  		for jdx := 0; jdx < len(podResA.Containers); jdx++ {
   744  			cntA := podResA.Containers[jdx]
   745  			cntB := podResB.Containers[jdx]
   746  
   747  			if cntA.Name != cntB.Name {
   748  				return false
   749  			}
   750  			if !equalInt64s(cntA.CpuIds, cntB.CpuIds) {
   751  				return false
   752  			}
   753  
   754  			if !equalContainerDevices(cntA.Devices, cntB.Devices) {
   755  				return false
   756  			}
   757  
   758  			if !equalDynamicResources(cntA.DynamicResources, cntB.DynamicResources) {
   759  				return false
   760  			}
   761  		}
   762  	}
   763  	return true
   764  }
   765  
   766  func equalDynamicResources(draResA, draResB []*podresourcesapi.DynamicResource) bool {
   767  	if len(draResA) != len(draResB) {
   768  		return false
   769  	}
   770  
   771  	for idx := 0; idx < len(draResA); idx++ {
   772  		cntDraResA := draResA[idx]
   773  		cntDraResB := draResB[idx]
   774  
   775  		if cntDraResA.ClassName != cntDraResB.ClassName {
   776  			return false
   777  		}
   778  		if cntDraResA.ClaimName != cntDraResB.ClaimName {
   779  			return false
   780  		}
   781  		if cntDraResA.ClaimNamespace != cntDraResB.ClaimNamespace {
   782  			return false
   783  		}
   784  		if len(cntDraResA.ClaimResources) != len(cntDraResB.ClaimResources) {
   785  			return false
   786  		}
   787  		for i := 0; i < len(cntDraResA.ClaimResources); i++ {
   788  			claimResA := cntDraResA.ClaimResources[i]
   789  			claimResB := cntDraResB.ClaimResources[i]
   790  			if len(claimResA.CDIDevices) != len(claimResB.CDIDevices) {
   791  				return false
   792  			}
   793  			for y := 0; y < len(claimResA.CDIDevices); y++ {
   794  				cdiDeviceA := claimResA.CDIDevices[y]
   795  				cdiDeviceB := claimResB.CDIDevices[y]
   796  				if cdiDeviceA.Name != cdiDeviceB.Name {
   797  					return false
   798  				}
   799  			}
   800  		}
   801  	}
   802  
   803  	return true
   804  }
   805  
   806  func equalContainerDevices(devA, devB []*podresourcesapi.ContainerDevices) bool {
   807  	if len(devA) != len(devB) {
   808  		return false
   809  	}
   810  
   811  	for idx := 0; idx < len(devA); idx++ {
   812  		cntDevA := devA[idx]
   813  		cntDevB := devB[idx]
   814  
   815  		if cntDevA.ResourceName != cntDevB.ResourceName {
   816  			return false
   817  		}
   818  		if !equalTopology(cntDevA.Topology, cntDevB.Topology) {
   819  			return false
   820  		}
   821  		if !equalStrings(cntDevA.DeviceIds, cntDevB.DeviceIds) {
   822  			return false
   823  		}
   824  	}
   825  
   826  	return true
   827  }
   828  
   829  func equalInt64s(a, b []int64) bool {
   830  	if len(a) != len(b) {
   831  		return false
   832  	}
   833  	aCopy := append([]int64{}, a...)
   834  	sort.Slice(aCopy, func(i, j int) bool { return aCopy[i] < aCopy[j] })
   835  	bCopy := append([]int64{}, b...)
   836  	sort.Slice(bCopy, func(i, j int) bool { return bCopy[i] < bCopy[j] })
   837  	return reflect.DeepEqual(aCopy, bCopy)
   838  }
   839  
   840  func equalStrings(a, b []string) bool {
   841  	if len(a) != len(b) {
   842  		return false
   843  	}
   844  	aCopy := append([]string{}, a...)
   845  	sort.Strings(aCopy)
   846  	bCopy := append([]string{}, b...)
   847  	sort.Strings(bCopy)
   848  	return reflect.DeepEqual(aCopy, bCopy)
   849  }
   850  
   851  func equalTopology(a, b *podresourcesapi.TopologyInfo) bool {
   852  	if a == nil && b != nil {
   853  		return false
   854  	}
   855  	if a != nil && b == nil {
   856  		return false
   857  	}
   858  	return reflect.DeepEqual(a, b)
   859  }
   860  
   861  func equalAllocatableResourcesResponse(respA, respB *podresourcesapi.AllocatableResourcesResponse) bool {
   862  	if !equalInt64s(respA.CpuIds, respB.CpuIds) {
   863  		return false
   864  	}
   865  	return equalContainerDevices(respA.Devices, respB.Devices)
   866  }
   867  
   868  func equalGetResponse(ResA, ResB *podresourcesapi.GetPodResourcesResponse) bool {
   869  	podResA := ResA.PodResources
   870  	podResB := ResB.PodResources
   871  	if podResA.Name != podResB.Name {
   872  		return false
   873  	}
   874  	if podResA.Namespace != podResB.Namespace {
   875  		return false
   876  	}
   877  	if len(podResA.Containers) != len(podResB.Containers) {
   878  		return false
   879  	}
   880  	for jdx := 0; jdx < len(podResA.Containers); jdx++ {
   881  		cntA := podResA.Containers[jdx]
   882  		cntB := podResB.Containers[jdx]
   883  
   884  		if cntA.Name != cntB.Name {
   885  			return false
   886  		}
   887  		if !equalInt64s(cntA.CpuIds, cntB.CpuIds) {
   888  			return false
   889  		}
   890  
   891  		if !equalContainerDevices(cntA.Devices, cntB.Devices) {
   892  			return false
   893  		}
   894  
   895  		if !equalDynamicResources(cntA.DynamicResources, cntB.DynamicResources) {
   896  			return false
   897  		}
   898  
   899  	}
   900  	return true
   901  }