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 }