@@ -4280,7 +4280,321 @@ func TestOVNKubernetesScriptLibGatewayInterface(t *testing.T) {
4280
4280
// Validate that gateway_mode_flags uses the variable
4281
4281
g .Expect (scriptData ).To (ContainSubstring ("--gateway-interface ${gateway_interface}" ),
4282
4282
"Script should use gateway_interface variable in gateway_mode_flags" )
4283
-
4283
+
4284
+ })
4285
+ }
4286
+ }
4287
+
4288
+ func TestGetNodeListByLabel (t * testing.T ) {
4289
+ tests := []struct {
4290
+ name string
4291
+ labelSelector string
4292
+ nodes []v1.Node
4293
+ expectedNodes []string
4294
+ expectError bool
4295
+ errorOnList bool
4296
+ }{
4297
+ {
4298
+ name : "nodes with specific label and value" ,
4299
+ labelSelector : "feature.node.kubernetes.io/dpu-enabled=true" ,
4300
+ nodes : []v1.Node {
4301
+ {
4302
+ ObjectMeta : metav1.ObjectMeta {
4303
+ Name : "node1" ,
4304
+ Labels : map [string ]string {
4305
+ "feature.node.kubernetes.io/dpu-enabled" : "true" ,
4306
+ },
4307
+ },
4308
+ },
4309
+ {
4310
+ ObjectMeta : metav1.ObjectMeta {
4311
+ Name : "node2" ,
4312
+ Labels : map [string ]string {
4313
+ "feature.node.kubernetes.io/dpu-enabled" : "false" ,
4314
+ },
4315
+ },
4316
+ },
4317
+ {
4318
+ ObjectMeta : metav1.ObjectMeta {
4319
+ Name : "node3" ,
4320
+ Labels : map [string ]string {},
4321
+ },
4322
+ },
4323
+ },
4324
+ expectedNodes : []string {"node1" },
4325
+ expectError : false ,
4326
+ },
4327
+ {
4328
+ name : "nodes with label key (any value)" ,
4329
+ labelSelector : "feature.node.kubernetes.io/dpu-enabled" ,
4330
+ nodes : []v1.Node {
4331
+ {
4332
+ ObjectMeta : metav1.ObjectMeta {
4333
+ Name : "node1" ,
4334
+ Labels : map [string ]string {
4335
+ "feature.node.kubernetes.io/dpu-enabled" : "true" ,
4336
+ },
4337
+ },
4338
+ },
4339
+ {
4340
+ ObjectMeta : metav1.ObjectMeta {
4341
+ Name : "node2" ,
4342
+ Labels : map [string ]string {
4343
+ "feature.node.kubernetes.io/dpu-enabled" : "false" ,
4344
+ },
4345
+ },
4346
+ },
4347
+ {
4348
+ ObjectMeta : metav1.ObjectMeta {
4349
+ Name : "node3" ,
4350
+ Labels : map [string ]string {},
4351
+ },
4352
+ },
4353
+ },
4354
+ expectedNodes : []string {"node1" , "node2" },
4355
+ expectError : false ,
4356
+ },
4357
+ {
4358
+ name : "nodes with label key only (no equals)" ,
4359
+ labelSelector : "feature.node.kubernetes.io/dpu-enabled" ,
4360
+ nodes : []v1.Node {
4361
+ {
4362
+ ObjectMeta : metav1.ObjectMeta {
4363
+ Name : "node1" ,
4364
+ Labels : map [string ]string {
4365
+ "feature.node.kubernetes.io/dpu-enabled" : "true" ,
4366
+ },
4367
+ },
4368
+ },
4369
+ {
4370
+ ObjectMeta : metav1.ObjectMeta {
4371
+ Name : "node2" ,
4372
+ Labels : map [string ]string {
4373
+ "feature.node.kubernetes.io/dpu-enabled" : "false" ,
4374
+ },
4375
+ },
4376
+ },
4377
+ {
4378
+ ObjectMeta : metav1.ObjectMeta {
4379
+ Name : "node3" ,
4380
+ Labels : map [string ]string {},
4381
+ },
4382
+ },
4383
+ },
4384
+ expectedNodes : []string {"node1" , "node2" },
4385
+ expectError : false ,
4386
+ },
4387
+ {
4388
+ name : "no nodes with matching label" ,
4389
+ labelSelector : "feature.node.kubernetes.io/dpu-enabled=true" ,
4390
+ nodes : []v1.Node {
4391
+ {
4392
+ ObjectMeta : metav1.ObjectMeta {
4393
+ Name : "node1" ,
4394
+ Labels : map [string ]string {
4395
+ "feature.node.kubernetes.io/dpu-enabled" : "false" ,
4396
+ },
4397
+ },
4398
+ },
4399
+ {
4400
+ ObjectMeta : metav1.ObjectMeta {
4401
+ Name : "node2" ,
4402
+ Labels : map [string ]string {},
4403
+ },
4404
+ },
4405
+ },
4406
+ expectedNodes : []string {},
4407
+ expectError : false ,
4408
+ },
4409
+ {
4410
+ name : "no nodes at all" ,
4411
+ labelSelector : "feature.node.kubernetes.io/dpu-enabled=true" ,
4412
+ nodes : []v1.Node {},
4413
+ expectedNodes : []string {},
4414
+ expectError : false ,
4415
+ },
4416
+ {
4417
+ name : "multiple nodes with different DPU labels" ,
4418
+ labelSelector : "network.operator.openshift.io/dpu-host" ,
4419
+ nodes : []v1.Node {
4420
+ {
4421
+ ObjectMeta : metav1.ObjectMeta {
4422
+ Name : "dpu-host-1" ,
4423
+ Labels : map [string ]string {
4424
+ "network.operator.openshift.io/dpu-host" : "" ,
4425
+ },
4426
+ },
4427
+ },
4428
+ {
4429
+ ObjectMeta : metav1.ObjectMeta {
4430
+ Name : "dpu-host-2" ,
4431
+ Labels : map [string ]string {
4432
+ "network.operator.openshift.io/dpu-host" : "enabled" ,
4433
+ },
4434
+ },
4435
+ },
4436
+ {
4437
+ ObjectMeta : metav1.ObjectMeta {
4438
+ Name : "regular-node" ,
4439
+ Labels : map [string ]string {
4440
+ "kubernetes.io/os" : "linux" ,
4441
+ },
4442
+ },
4443
+ },
4444
+ },
4445
+ expectedNodes : []string {"dpu-host-1" , "dpu-host-2" },
4446
+ expectError : false ,
4447
+ },
4448
+ {
4449
+ name : "nodes with empty label values" ,
4450
+ labelSelector : "feature.node.kubernetes.io/empty-label" ,
4451
+ nodes : []v1.Node {
4452
+ {
4453
+ ObjectMeta : metav1.ObjectMeta {
4454
+ Name : "node-with-empty-value" ,
4455
+ Labels : map [string ]string {
4456
+ "feature.node.kubernetes.io/empty-label" : "" ,
4457
+ },
4458
+ },
4459
+ },
4460
+ {
4461
+ ObjectMeta : metav1.ObjectMeta {
4462
+ Name : "node-with-value" ,
4463
+ Labels : map [string ]string {
4464
+ "feature.node.kubernetes.io/empty-label" : "some-value" ,
4465
+ },
4466
+ },
4467
+ },
4468
+ {
4469
+ ObjectMeta : metav1.ObjectMeta {
4470
+ Name : "node-without-label" ,
4471
+ Labels : map [string ]string {
4472
+ "other-label" : "other-value" ,
4473
+ },
4474
+ },
4475
+ },
4476
+ },
4477
+ expectedNodes : []string {"node-with-empty-value" , "node-with-value" },
4478
+ expectError : false ,
4479
+ },
4480
+ {
4481
+ name : "nodes with specific empty label value" ,
4482
+ labelSelector : "feature.node.kubernetes.io/empty-label=" ,
4483
+ nodes : []v1.Node {
4484
+ {
4485
+ ObjectMeta : metav1.ObjectMeta {
4486
+ Name : "node-with-empty-value" ,
4487
+ Labels : map [string ]string {
4488
+ "feature.node.kubernetes.io/empty-label" : "" ,
4489
+ },
4490
+ },
4491
+ },
4492
+ {
4493
+ ObjectMeta : metav1.ObjectMeta {
4494
+ Name : "node-with-value" ,
4495
+ Labels : map [string ]string {
4496
+ "feature.node.kubernetes.io/empty-label" : "some-value" ,
4497
+ },
4498
+ },
4499
+ },
4500
+ {
4501
+ ObjectMeta : metav1.ObjectMeta {
4502
+ Name : "node-without-label" ,
4503
+ Labels : map [string ]string {
4504
+ "other-label" : "other-value" ,
4505
+ },
4506
+ },
4507
+ },
4508
+ },
4509
+ expectedNodes : []string {"node-with-empty-value" },
4510
+ expectError : false ,
4511
+ },
4512
+ }
4513
+
4514
+ for _ , tt := range tests {
4515
+ t .Run (tt .name , func (t * testing.T ) {
4516
+ // Create fake client with test nodes - pass nodes to constructor
4517
+ // so they're available in both the typed and controller-runtime clients
4518
+ nodeObjs := make ([]crclient.Object , len (tt .nodes ))
4519
+ for i , node := range tt .nodes {
4520
+ nodeObjs [i ] = & node
4521
+ }
4522
+ client := cnofake .NewFakeClient (nodeObjs ... )
4523
+
4524
+ // Call function under test
4525
+ result , err := getNodeListByLabel (client , tt .labelSelector )
4526
+
4527
+ // Verify results
4528
+ if tt .expectError {
4529
+ assert .Error (t , err )
4530
+ assert .Nil (t , result )
4531
+ } else {
4532
+ assert .NoError (t , err )
4533
+ assert .ElementsMatch (t , tt .expectedNodes , result )
4534
+ }
4284
4535
})
4285
4536
}
4286
4537
}
4538
+
4539
+ func TestGetNodeListByLabel_RealWorldScenarios (t * testing.T ) {
4540
+ t .Run ("OpenShift DPU node scenarios" , func (t * testing.T ) {
4541
+ nodes := []v1.Node {
4542
+ {
4543
+ ObjectMeta : metav1.ObjectMeta {
4544
+ Name : "master-1" ,
4545
+ Labels : map [string ]string {
4546
+ "node-role.kubernetes.io/master" : "" ,
4547
+ "node-role.kubernetes.io/control-plane" : "" ,
4548
+ "network.operator.openshift.io/dpu-host" : "" ,
4549
+ "feature.node.kubernetes.io/dpu-enabled" : "true" ,
4550
+ },
4551
+ },
4552
+ },
4553
+ {
4554
+ ObjectMeta : metav1.ObjectMeta {
4555
+ Name : "worker-1" ,
4556
+ Labels : map [string ]string {
4557
+ "node-role.kubernetes.io/worker" : "" ,
4558
+ "network.operator.openshift.io/dpu" : "" ,
4559
+ "feature.node.kubernetes.io/dpu-enabled" : "true" ,
4560
+ },
4561
+ },
4562
+ },
4563
+ {
4564
+ ObjectMeta : metav1.ObjectMeta {
4565
+ Name : "worker-2" ,
4566
+ Labels : map [string ]string {
4567
+ "node-role.kubernetes.io/worker" : "" ,
4568
+ "kubernetes.io/os" : "linux" ,
4569
+ },
4570
+ },
4571
+ },
4572
+ }
4573
+
4574
+ // Create fake client with test nodes - pass nodes to constructor
4575
+ nodeObjs := make ([]crclient.Object , len (nodes ))
4576
+ for i , node := range nodes {
4577
+ nodeObjs [i ] = & node
4578
+ }
4579
+ client := cnofake .NewFakeClient (nodeObjs ... )
4580
+
4581
+ // Test various DPU-related label selectors
4582
+ testCases := []struct {
4583
+ selector string
4584
+ expected []string
4585
+ }{
4586
+ {"feature.node.kubernetes.io/dpu-enabled=true" , []string {"master-1" , "worker-1" }},
4587
+ {"feature.node.kubernetes.io/dpu-enabled" , []string {"master-1" , "worker-1" }},
4588
+ {"network.operator.openshift.io/dpu-host" , []string {"master-1" }},
4589
+ {"network.operator.openshift.io/dpu" , []string {"worker-1" }},
4590
+ {"node-role.kubernetes.io/worker" , []string {"worker-1" , "worker-2" }},
4591
+ {"nonexistent-label" , []string {}},
4592
+ }
4593
+
4594
+ for _ , tc := range testCases {
4595
+ result , err := getNodeListByLabel (client , tc .selector )
4596
+ assert .NoError (t , err , "Failed for selector: %s" , tc .selector )
4597
+ assert .ElementsMatch (t , tc .expected , result , "Failed for selector: %s" , tc .selector )
4598
+ }
4599
+ })
4600
+ }
0 commit comments