sigs.k8s.io/kueue@v0.6.2/pkg/config/config_test.go (about) 1 /* 2 Copyright 2023 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 config 18 19 import ( 20 "errors" 21 "io/fs" 22 "os" 23 "path/filepath" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/google/go-cmp/cmp/cmpopts" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/util/yaml" 32 "k8s.io/client-go/tools/leaderelection/resourcelock" 33 "k8s.io/utils/ptr" 34 ctrl "sigs.k8s.io/controller-runtime" 35 ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" 36 runtimeconfig "sigs.k8s.io/controller-runtime/pkg/config" 37 metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" 38 "sigs.k8s.io/controller-runtime/pkg/webhook" 39 40 configapi "sigs.k8s.io/kueue/apis/config/v1beta1" 41 "sigs.k8s.io/kueue/pkg/controller/jobs/job" 42 ) 43 44 func TestLoad(t *testing.T) { 45 test_scheme := runtime.NewScheme() 46 err := configapi.AddToScheme(test_scheme) 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 // temp dir 52 tmpDir, err := os.MkdirTemp("", "temp") 53 if err != nil { 54 t.Fatal(err) 55 } 56 defer os.RemoveAll(tmpDir) 57 58 namespaceOverWriteConfig := filepath.Join(tmpDir, "namespace-overwrite.yaml") 59 if err := os.WriteFile(namespaceOverWriteConfig, []byte(` 60 apiVersion: config.kueue.x-k8s.io/v1beta1 61 kind: Configuration 62 namespace: kueue-tenant-a 63 health: 64 healthProbeBindAddress: :8081 65 metrics: 66 bindAddress: :8080 67 leaderElection: 68 leaderElect: true 69 resourceName: c1f6bfd2.kueue.x-k8s.io 70 webhook: 71 port: 9443 72 `), os.FileMode(0600)); err != nil { 73 t.Fatal(err) 74 } 75 76 ctrlManagerConfigSpecOverWriteConfig := filepath.Join(tmpDir, "ctrl-manager-config-spec-overwrite.yaml") 77 if err := os.WriteFile(ctrlManagerConfigSpecOverWriteConfig, []byte(` 78 apiVersion: config.kueue.x-k8s.io/v1beta1 79 kind: Configuration 80 namespace: kueue-system 81 health: 82 healthProbeBindAddress: :38081 83 metrics: 84 bindAddress: :38080 85 leaderElection: 86 leaderElect: true 87 resourceName: test-id 88 webhook: 89 port: 9444 90 `), os.FileMode(0600)); err != nil { 91 t.Fatal(err) 92 } 93 94 certOverWriteConfig := filepath.Join(tmpDir, "cert-overwrite.yaml") 95 if err := os.WriteFile(certOverWriteConfig, []byte(` 96 apiVersion: config.kueue.x-k8s.io/v1beta1 97 kind: Configuration 98 namespace: kueue-system 99 health: 100 healthProbeBindAddress: :8081 101 metrics: 102 bindAddress: :8080 103 leaderElection: 104 leaderElect: true 105 resourceName: c1f6bfd2.kueue.x-k8s.io 106 webhook: 107 port: 9443 108 internalCertManagement: 109 enable: true 110 webhookServiceName: kueue-tenant-a-webhook-service 111 webhookSecretName: kueue-tenant-a-webhook-server-cert 112 `), os.FileMode(0600)); err != nil { 113 t.Fatal(err) 114 } 115 116 disableCertOverWriteConfig := filepath.Join(tmpDir, "disable-cert-overwrite.yaml") 117 if err := os.WriteFile(disableCertOverWriteConfig, []byte(` 118 apiVersion: config.kueue.x-k8s.io/v1beta1 119 kind: Configuration 120 namespace: kueue-system 121 health: 122 healthProbeBindAddress: :8081 123 metrics: 124 bindAddress: :8080 125 leaderElection: 126 leaderElect: true 127 resourceName: c1f6bfd2.kueue.x-k8s.io 128 webhook: 129 port: 9443 130 internalCertManagement: 131 enable: false 132 `), os.FileMode(0600)); err != nil { 133 t.Fatal(err) 134 } 135 136 leaderElectionDisabledConfig := filepath.Join(tmpDir, "leaderElection-disabled.yaml") 137 if err := os.WriteFile(leaderElectionDisabledConfig, []byte(` 138 apiVersion: config.kueue.x-k8s.io/v1beta1 139 kind: Configuration 140 namespace: kueue-system 141 health: 142 healthProbeBindAddress: :8081 143 metrics: 144 bindAddress: :8080 145 leaderElection: 146 leaderElect: false 147 webhook: 148 port: 9443 149 `), os.FileMode(0600)); err != nil { 150 t.Fatal(err) 151 } 152 153 waitForPodsReadyEnabledConfig := filepath.Join(tmpDir, "waitForPodsReady-enabled.yaml") 154 if err := os.WriteFile(waitForPodsReadyEnabledConfig, []byte(` 155 apiVersion: config.kueue.x-k8s.io/v1beta1 156 kind: Configuration 157 waitForPodsReady: 158 enable: true 159 requeuingStrategy: 160 timestamp: Creation 161 backoffLimitCount: 10 162 `), os.FileMode(0600)); err != nil { 163 t.Fatal(err) 164 } 165 166 clientConnectionConfig := filepath.Join(tmpDir, "clientConnection.yaml") 167 if err := os.WriteFile(clientConnectionConfig, []byte(` 168 apiVersion: config.kueue.x-k8s.io/v1beta1 169 kind: Configuration 170 namespace: kueue-system 171 health: 172 healthProbeBindAddress: :8081 173 metrics: 174 bindAddress: :8080 175 leaderElection: 176 leaderElect: true 177 resourceName: c1f6bfd2.kueue.x-k8s.io 178 webhook: 179 port: 9443 180 clientConnection: 181 qps: 50 182 burst: 100 183 `), os.FileMode(0600)); err != nil { 184 t.Fatal(err) 185 } 186 187 fullControllerConfig := filepath.Join(tmpDir, "fullControllerConfig.yaml") 188 if err := os.WriteFile(fullControllerConfig, []byte(` 189 apiVersion: config.kueue.x-k8s.io/v1beta1 190 kind: Configuration 191 namespace: kueue-system 192 health: 193 healthProbeBindAddress: :8081 194 readinessEndpointName: ready 195 livenessEndpointName: live 196 metrics: 197 bindAddress: :8080 198 pprofBindAddress: :8082 199 leaderElection: 200 leaderElect: true 201 resourceName: c1f6bfd2.kueue.x-k8s.io 202 resourceNamespace: namespace 203 resourceLock: lock 204 leaseDuration: 100s 205 renewDeadline: 15s 206 retryPeriod: 30s 207 webhook: 208 port: 9443 209 host: host 210 certDir: certDir 211 controller: 212 groupKindConcurrency: 213 workload: 5 214 cacheSyncTimeout: 3 215 clientConnection: 216 qps: 50 217 burst: 100 218 `), os.FileMode(0600)); err != nil { 219 t.Fatal(err) 220 } 221 integrationsConfig := filepath.Join(tmpDir, "integrations.yaml") 222 if err := os.WriteFile(integrationsConfig, []byte(` 223 apiVersion: config.kueue.x-k8s.io/v1beta1 224 kind: Configuration 225 integrations: 226 frameworks: 227 - batch/job 228 `), os.FileMode(0600)); err != nil { 229 t.Fatal(err) 230 } 231 queueVisibilityConfig := filepath.Join(tmpDir, "queueVisibility.yaml") 232 if err := os.WriteFile(queueVisibilityConfig, []byte(` 233 apiVersion: config.kueue.x-k8s.io/v1beta1 234 kind: Configuration 235 queueVisibility: 236 updateIntervalSeconds: 10 237 clusterQueues: 238 maxCount: 0 239 `), os.FileMode(0600)); err != nil { 240 t.Fatal(err) 241 } 242 podIntegrationOptionsConfig := filepath.Join(tmpDir, "podIntegrationOptions.yaml") 243 if err := os.WriteFile(podIntegrationOptionsConfig, []byte(` 244 apiVersion: config.kueue.x-k8s.io/v1beta1 245 kind: Configuration 246 integrations: 247 frameworks: 248 - pod 249 podOptions: 250 namespaceSelector: 251 matchExpressions: 252 - key: kubernetes.io/metadata.name 253 operator: NotIn 254 values: [ kube-system, kueue-system, prohibited-namespace ] 255 podSelector: 256 matchExpressions: 257 - key: kueue-job 258 operator: In 259 values: [ "true", "True", "yes" ] 260 `), os.FileMode(0600)); err != nil { 261 t.Fatal(err) 262 } 263 264 multiKueueConfig := filepath.Join(tmpDir, "multiKueue.yaml") 265 if err := os.WriteFile(multiKueueConfig, []byte(` 266 apiVersion: config.kueue.x-k8s.io/v1beta1 267 kind: Configuration 268 namespace: kueue-system 269 multiKueue: 270 gcInterval: 1m30s 271 origin: multikueue-manager1 272 `), os.FileMode(0600)); err != nil { 273 t.Fatal(err) 274 } 275 defaultControlOptions := ctrl.Options{ 276 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 277 Metrics: metricsserver.Options{ 278 BindAddress: configapi.DefaultMetricsBindAddress, 279 }, 280 LeaderElection: true, 281 LeaderElectionID: configapi.DefaultLeaderElectionID, 282 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 283 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 284 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 285 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 286 WebhookServer: &webhook.DefaultServer{ 287 Options: webhook.Options{ 288 Port: configapi.DefaultWebhookPort, 289 }, 290 }, 291 } 292 293 enableDefaultInternalCertManagement := &configapi.InternalCertManagement{ 294 Enable: ptr.To(true), 295 WebhookServiceName: ptr.To(configapi.DefaultWebhookServiceName), 296 WebhookSecretName: ptr.To(configapi.DefaultWebhookSecretName), 297 } 298 299 ctrlOptsCmpOpts := []cmp.Option{ 300 cmpopts.IgnoreUnexported(ctrl.Options{}), 301 cmpopts.IgnoreUnexported(webhook.DefaultServer{}), 302 cmpopts.IgnoreUnexported(ctrlcache.Options{}), 303 cmpopts.IgnoreFields(ctrl.Options{}, "Scheme", "Logger"), 304 } 305 306 // Ignore the controller manager section since it's side effect is checked against 307 // the content of the resulting options 308 configCmpOpts := []cmp.Option{ 309 cmpopts.IgnoreFields(configapi.Configuration{}, "ControllerManager"), 310 } 311 312 defaultClientConnection := &configapi.ClientConnection{ 313 QPS: ptr.To[float32](configapi.DefaultClientConnectionQPS), 314 Burst: ptr.To[int32](configapi.DefaultClientConnectionBurst), 315 } 316 317 defaultIntegrations := &configapi.Integrations{ 318 Frameworks: []string{job.FrameworkName}, 319 PodOptions: &configapi.PodIntegrationOptions{ 320 NamespaceSelector: &metav1.LabelSelector{ 321 MatchExpressions: []metav1.LabelSelectorRequirement{ 322 { 323 Key: "kubernetes.io/metadata.name", 324 Operator: metav1.LabelSelectorOpNotIn, 325 Values: []string{"kube-system", "kueue-system"}, 326 }, 327 }, 328 }, 329 PodSelector: &metav1.LabelSelector{}, 330 }, 331 } 332 333 defaultQueueVisibility := &configapi.QueueVisibility{ 334 UpdateIntervalSeconds: configapi.DefaultQueueVisibilityUpdateIntervalSeconds, 335 ClusterQueues: &configapi.ClusterQueueVisibility{ 336 MaxCount: 10, 337 }, 338 } 339 340 defaultMultiKueue := &configapi.MultiKueue{ 341 GCInterval: &metav1.Duration{Duration: configapi.DefaultMultiKueueGCInterval}, 342 Origin: ptr.To(configapi.DefaultMultiKueueOrigin), 343 } 344 345 testcases := []struct { 346 name string 347 configFile string 348 wantConfiguration configapi.Configuration 349 wantOptions ctrl.Options 350 wantError error 351 }{ 352 { 353 name: "default config", 354 configFile: "", 355 wantConfiguration: configapi.Configuration{ 356 Namespace: ptr.To(configapi.DefaultNamespace), 357 InternalCertManagement: enableDefaultInternalCertManagement, 358 ClientConnection: defaultClientConnection, 359 Integrations: defaultIntegrations, 360 QueueVisibility: defaultQueueVisibility, 361 MultiKueue: defaultMultiKueue, 362 }, 363 wantOptions: ctrl.Options{ 364 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 365 Metrics: metricsserver.Options{ 366 BindAddress: configapi.DefaultMetricsBindAddress, 367 }, 368 LeaderElection: true, 369 LeaderElectionID: configapi.DefaultLeaderElectionID, 370 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 371 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 372 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 373 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 374 WebhookServer: &webhook.DefaultServer{ 375 Options: webhook.Options{ 376 Port: configapi.DefaultWebhookPort, 377 }, 378 }, 379 }, 380 }, 381 { 382 name: "bad path", 383 configFile: ".", 384 wantError: &fs.PathError{ 385 Op: "read", 386 Path: ".", 387 Err: errors.New("is a directory"), 388 }, 389 }, 390 { 391 name: "namespace overwrite config", 392 configFile: namespaceOverWriteConfig, 393 wantConfiguration: configapi.Configuration{ 394 TypeMeta: metav1.TypeMeta{ 395 APIVersion: configapi.GroupVersion.String(), 396 Kind: "Configuration", 397 }, 398 Namespace: ptr.To("kueue-tenant-a"), 399 ManageJobsWithoutQueueName: false, 400 InternalCertManagement: enableDefaultInternalCertManagement, 401 ClientConnection: defaultClientConnection, 402 Integrations: &configapi.Integrations{ 403 Frameworks: []string{job.FrameworkName}, 404 PodOptions: &configapi.PodIntegrationOptions{ 405 NamespaceSelector: &metav1.LabelSelector{ 406 MatchExpressions: []metav1.LabelSelectorRequirement{ 407 { 408 Key: "kubernetes.io/metadata.name", 409 Operator: metav1.LabelSelectorOpNotIn, 410 Values: []string{"kube-system", "kueue-tenant-a"}, 411 }, 412 }, 413 }, 414 PodSelector: &metav1.LabelSelector{}, 415 }, 416 }, 417 QueueVisibility: defaultQueueVisibility, 418 MultiKueue: defaultMultiKueue, 419 }, 420 wantOptions: defaultControlOptions, 421 }, 422 { 423 name: "ControllerManagerConfigurationSpec overwrite config", 424 configFile: ctrlManagerConfigSpecOverWriteConfig, 425 wantConfiguration: configapi.Configuration{ 426 TypeMeta: metav1.TypeMeta{ 427 APIVersion: configapi.GroupVersion.String(), 428 Kind: "Configuration", 429 }, 430 Namespace: ptr.To(configapi.DefaultNamespace), 431 ManageJobsWithoutQueueName: false, 432 InternalCertManagement: enableDefaultInternalCertManagement, 433 ClientConnection: defaultClientConnection, 434 Integrations: defaultIntegrations, 435 QueueVisibility: defaultQueueVisibility, 436 MultiKueue: defaultMultiKueue, 437 }, 438 wantOptions: ctrl.Options{ 439 HealthProbeBindAddress: ":38081", 440 Metrics: metricsserver.Options{ 441 BindAddress: ":38080", 442 }, 443 LeaderElection: true, 444 LeaderElectionID: "test-id", 445 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 446 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 447 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 448 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 449 WebhookServer: &webhook.DefaultServer{ 450 Options: webhook.Options{ 451 Port: 9444, 452 }, 453 }, 454 }, 455 }, 456 { 457 name: "cert options overwrite config", 458 configFile: certOverWriteConfig, 459 wantConfiguration: configapi.Configuration{ 460 TypeMeta: metav1.TypeMeta{ 461 APIVersion: configapi.GroupVersion.String(), 462 Kind: "Configuration", 463 }, 464 Namespace: ptr.To(configapi.DefaultNamespace), 465 ManageJobsWithoutQueueName: false, 466 InternalCertManagement: &configapi.InternalCertManagement{ 467 Enable: ptr.To(true), 468 WebhookServiceName: ptr.To("kueue-tenant-a-webhook-service"), 469 WebhookSecretName: ptr.To("kueue-tenant-a-webhook-server-cert"), 470 }, 471 ClientConnection: defaultClientConnection, 472 Integrations: defaultIntegrations, 473 QueueVisibility: defaultQueueVisibility, 474 MultiKueue: defaultMultiKueue, 475 }, 476 wantOptions: defaultControlOptions, 477 }, 478 { 479 name: "disable cert overwrite config", 480 configFile: disableCertOverWriteConfig, 481 wantConfiguration: configapi.Configuration{ 482 TypeMeta: metav1.TypeMeta{ 483 APIVersion: configapi.GroupVersion.String(), 484 Kind: "Configuration", 485 }, 486 Namespace: ptr.To(configapi.DefaultNamespace), 487 ManageJobsWithoutQueueName: false, 488 InternalCertManagement: &configapi.InternalCertManagement{ 489 Enable: ptr.To(false), 490 }, 491 ClientConnection: defaultClientConnection, 492 Integrations: defaultIntegrations, 493 QueueVisibility: defaultQueueVisibility, 494 MultiKueue: defaultMultiKueue, 495 }, 496 wantOptions: defaultControlOptions, 497 }, 498 { 499 name: "leaderElection disabled config", 500 configFile: leaderElectionDisabledConfig, 501 wantConfiguration: configapi.Configuration{ 502 TypeMeta: metav1.TypeMeta{ 503 APIVersion: configapi.GroupVersion.String(), 504 Kind: "Configuration", 505 }, 506 Namespace: ptr.To("kueue-system"), 507 ManageJobsWithoutQueueName: false, 508 InternalCertManagement: enableDefaultInternalCertManagement, 509 ClientConnection: defaultClientConnection, 510 Integrations: defaultIntegrations, 511 QueueVisibility: defaultQueueVisibility, 512 MultiKueue: defaultMultiKueue, 513 }, 514 515 wantOptions: ctrl.Options{ 516 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 517 Metrics: metricsserver.Options{ 518 BindAddress: configapi.DefaultMetricsBindAddress, 519 }, 520 LeaderElectionID: configapi.DefaultLeaderElectionID, 521 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 522 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 523 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 524 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 525 LeaderElection: false, 526 WebhookServer: &webhook.DefaultServer{ 527 Options: webhook.Options{ 528 Port: configapi.DefaultWebhookPort, 529 }, 530 }, 531 }, 532 }, 533 { 534 name: "enable waitForPodsReady config", 535 configFile: waitForPodsReadyEnabledConfig, 536 wantConfiguration: configapi.Configuration{ 537 TypeMeta: metav1.TypeMeta{ 538 APIVersion: configapi.GroupVersion.String(), 539 Kind: "Configuration", 540 }, 541 Namespace: ptr.To(configapi.DefaultNamespace), 542 ManageJobsWithoutQueueName: false, 543 InternalCertManagement: enableDefaultInternalCertManagement, 544 WaitForPodsReady: &configapi.WaitForPodsReady{ 545 Enable: true, 546 BlockAdmission: ptr.To(true), 547 Timeout: &metav1.Duration{Duration: 5 * time.Minute}, 548 RequeuingStrategy: &configapi.RequeuingStrategy{ 549 Timestamp: ptr.To(configapi.CreationTimestamp), 550 BackoffLimitCount: ptr.To[int32](10), 551 }, 552 }, 553 ClientConnection: defaultClientConnection, 554 Integrations: defaultIntegrations, 555 QueueVisibility: defaultQueueVisibility, 556 MultiKueue: defaultMultiKueue, 557 }, 558 wantOptions: ctrl.Options{ 559 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 560 Metrics: metricsserver.Options{ 561 BindAddress: configapi.DefaultMetricsBindAddress, 562 }, 563 LeaderElection: true, 564 LeaderElectionID: configapi.DefaultLeaderElectionID, 565 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 566 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 567 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 568 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 569 WebhookServer: &webhook.DefaultServer{ 570 Options: webhook.Options{ 571 Port: configapi.DefaultWebhookPort, 572 }, 573 }, 574 }, 575 }, 576 { 577 name: "clientConnection config", 578 configFile: clientConnectionConfig, 579 wantConfiguration: configapi.Configuration{ 580 TypeMeta: metav1.TypeMeta{ 581 APIVersion: configapi.GroupVersion.String(), 582 Kind: "Configuration", 583 }, 584 Namespace: ptr.To(configapi.DefaultNamespace), 585 ManageJobsWithoutQueueName: false, 586 InternalCertManagement: enableDefaultInternalCertManagement, 587 ClientConnection: &configapi.ClientConnection{ 588 QPS: ptr.To[float32](50), 589 Burst: ptr.To[int32](100), 590 }, 591 Integrations: defaultIntegrations, 592 QueueVisibility: defaultQueueVisibility, 593 MultiKueue: defaultMultiKueue, 594 }, 595 wantOptions: defaultControlOptions, 596 }, 597 { 598 name: "fullController config", 599 configFile: fullControllerConfig, 600 wantConfiguration: configapi.Configuration{ 601 TypeMeta: metav1.TypeMeta{ 602 APIVersion: configapi.GroupVersion.String(), 603 Kind: "Configuration", 604 }, 605 Namespace: ptr.To(configapi.DefaultNamespace), 606 ManageJobsWithoutQueueName: false, 607 InternalCertManagement: enableDefaultInternalCertManagement, 608 ClientConnection: &configapi.ClientConnection{ 609 QPS: ptr.To[float32](50), 610 Burst: ptr.To[int32](100), 611 }, 612 Integrations: defaultIntegrations, 613 QueueVisibility: defaultQueueVisibility, 614 MultiKueue: defaultMultiKueue, 615 }, 616 wantOptions: ctrl.Options{ 617 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 618 ReadinessEndpointName: "ready", 619 LivenessEndpointName: "live", 620 Metrics: metricsserver.Options{ 621 BindAddress: configapi.DefaultMetricsBindAddress, 622 }, 623 PprofBindAddress: ":8082", 624 LeaderElection: true, 625 LeaderElectionID: configapi.DefaultLeaderElectionID, 626 LeaderElectionNamespace: "namespace", 627 LeaderElectionResourceLock: "lock", 628 LeaseDuration: ptr.To(time.Second * 100), 629 RenewDeadline: ptr.To(time.Second * 15), 630 RetryPeriod: ptr.To(time.Second * 30), 631 Controller: runtimeconfig.Controller{ 632 GroupKindConcurrency: map[string]int{ 633 "workload": 5, 634 }, 635 CacheSyncTimeout: 3, 636 }, 637 WebhookServer: &webhook.DefaultServer{ 638 Options: webhook.Options{ 639 Port: configapi.DefaultWebhookPort, 640 Host: "host", 641 CertDir: "certDir", 642 }, 643 }, 644 }, 645 }, 646 { 647 name: "integrations config", 648 configFile: integrationsConfig, 649 wantConfiguration: configapi.Configuration{ 650 TypeMeta: metav1.TypeMeta{ 651 APIVersion: configapi.GroupVersion.String(), 652 Kind: "Configuration", 653 }, 654 Namespace: ptr.To(configapi.DefaultNamespace), 655 ManageJobsWithoutQueueName: false, 656 InternalCertManagement: enableDefaultInternalCertManagement, 657 ClientConnection: defaultClientConnection, 658 Integrations: &configapi.Integrations{ 659 // referencing job.FrameworkName ensures the link of job package 660 // therefore the batch/framework should be registered 661 Frameworks: []string{job.FrameworkName}, 662 PodOptions: &configapi.PodIntegrationOptions{ 663 NamespaceSelector: &metav1.LabelSelector{ 664 MatchExpressions: []metav1.LabelSelectorRequirement{ 665 { 666 Key: "kubernetes.io/metadata.name", 667 Operator: metav1.LabelSelectorOpNotIn, 668 Values: []string{"kube-system", "kueue-system"}, 669 }, 670 }, 671 }, 672 PodSelector: &metav1.LabelSelector{}, 673 }, 674 }, 675 QueueVisibility: defaultQueueVisibility, 676 MultiKueue: defaultMultiKueue, 677 }, 678 wantOptions: ctrl.Options{ 679 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 680 Metrics: metricsserver.Options{ 681 BindAddress: configapi.DefaultMetricsBindAddress, 682 }, 683 LeaderElection: true, 684 LeaderElectionID: configapi.DefaultLeaderElectionID, 685 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 686 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 687 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 688 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 689 WebhookServer: &webhook.DefaultServer{ 690 Options: webhook.Options{ 691 Port: configapi.DefaultWebhookPort, 692 }, 693 }, 694 }, 695 }, 696 { 697 name: "queue visibility config", 698 configFile: queueVisibilityConfig, 699 wantConfiguration: configapi.Configuration{ 700 TypeMeta: metav1.TypeMeta{ 701 APIVersion: configapi.GroupVersion.String(), 702 Kind: "Configuration", 703 }, 704 Namespace: ptr.To(configapi.DefaultNamespace), 705 ManageJobsWithoutQueueName: false, 706 InternalCertManagement: enableDefaultInternalCertManagement, 707 ClientConnection: defaultClientConnection, 708 Integrations: defaultIntegrations, 709 QueueVisibility: &configapi.QueueVisibility{ 710 UpdateIntervalSeconds: 10, 711 ClusterQueues: &configapi.ClusterQueueVisibility{ 712 MaxCount: 0, 713 }, 714 }, 715 MultiKueue: defaultMultiKueue, 716 }, 717 wantOptions: ctrl.Options{ 718 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 719 Metrics: metricsserver.Options{ 720 BindAddress: configapi.DefaultMetricsBindAddress, 721 }, 722 LeaderElection: true, 723 LeaderElectionID: configapi.DefaultLeaderElectionID, 724 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 725 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 726 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 727 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 728 WebhookServer: &webhook.DefaultServer{ 729 Options: webhook.Options{ 730 Port: configapi.DefaultWebhookPort, 731 }, 732 }, 733 }, 734 }, 735 { 736 name: "pod integration options config", 737 configFile: podIntegrationOptionsConfig, 738 wantConfiguration: configapi.Configuration{ 739 TypeMeta: metav1.TypeMeta{ 740 APIVersion: configapi.GroupVersion.String(), 741 Kind: "Configuration", 742 }, 743 Namespace: ptr.To(configapi.DefaultNamespace), 744 ManageJobsWithoutQueueName: false, 745 InternalCertManagement: enableDefaultInternalCertManagement, 746 ClientConnection: defaultClientConnection, 747 QueueVisibility: defaultQueueVisibility, 748 Integrations: &configapi.Integrations{ 749 Frameworks: []string{ 750 "pod", 751 }, 752 PodOptions: &configapi.PodIntegrationOptions{ 753 NamespaceSelector: &metav1.LabelSelector{ 754 MatchExpressions: []metav1.LabelSelectorRequirement{ 755 { 756 Key: "kubernetes.io/metadata.name", 757 Operator: metav1.LabelSelectorOpNotIn, 758 Values: []string{"kube-system", "kueue-system", "prohibited-namespace"}, 759 }, 760 }, 761 }, 762 PodSelector: &metav1.LabelSelector{ 763 MatchExpressions: []metav1.LabelSelectorRequirement{ 764 { 765 Key: "kueue-job", 766 Operator: metav1.LabelSelectorOpIn, 767 Values: []string{"true", "True", "yes"}, 768 }, 769 }, 770 }, 771 }, 772 }, 773 MultiKueue: defaultMultiKueue, 774 }, 775 wantOptions: ctrl.Options{ 776 HealthProbeBindAddress: configapi.DefaultHealthProbeBindAddress, 777 Metrics: metricsserver.Options{ 778 BindAddress: configapi.DefaultMetricsBindAddress, 779 }, 780 LeaderElection: true, 781 LeaderElectionID: configapi.DefaultLeaderElectionID, 782 LeaderElectionResourceLock: resourcelock.LeasesResourceLock, 783 LeaseDuration: ptr.To(configapi.DefaultLeaderElectionLeaseDuration), 784 RenewDeadline: ptr.To(configapi.DefaultLeaderElectionRenewDeadline), 785 RetryPeriod: ptr.To(configapi.DefaultLeaderElectionRetryPeriod), 786 WebhookServer: &webhook.DefaultServer{ 787 Options: webhook.Options{ 788 Port: configapi.DefaultWebhookPort, 789 }, 790 }, 791 }, 792 }, 793 { 794 name: "multiKueue config", 795 configFile: multiKueueConfig, 796 wantConfiguration: configapi.Configuration{ 797 TypeMeta: metav1.TypeMeta{ 798 APIVersion: configapi.GroupVersion.String(), 799 Kind: "Configuration", 800 }, 801 Namespace: ptr.To(configapi.DefaultNamespace), 802 ManageJobsWithoutQueueName: false, 803 InternalCertManagement: enableDefaultInternalCertManagement, 804 ClientConnection: defaultClientConnection, 805 Integrations: defaultIntegrations, 806 QueueVisibility: defaultQueueVisibility, 807 MultiKueue: &configapi.MultiKueue{ 808 GCInterval: &metav1.Duration{Duration: 90 * time.Second}, 809 Origin: ptr.To("multikueue-manager1"), 810 }, 811 }, 812 wantOptions: defaultControlOptions, 813 }, 814 } 815 816 for _, tc := range testcases { 817 t.Run(tc.name, func(t *testing.T) { 818 options, cfg, err := Load(test_scheme, tc.configFile) 819 if tc.wantError == nil { 820 if err != nil { 821 t.Errorf("Unexpected error:%s", err) 822 } 823 if diff := cmp.Diff(tc.wantConfiguration, cfg, configCmpOpts...); diff != "" { 824 t.Errorf("Unexpected config (-want +got):\n%s", diff) 825 } 826 if diff := cmp.Diff(tc.wantOptions, options, ctrlOptsCmpOpts...); diff != "" { 827 t.Errorf("Unexpected options (-want +got):\n%s", diff) 828 } 829 } else { 830 if diff := cmp.Diff(tc.wantError.Error(), err.Error()); diff != "" { 831 t.Errorf("Unexpected error (-want +got):\n%s", diff) 832 } 833 } 834 }) 835 } 836 } 837 838 func TestEncode(t *testing.T) { 839 test_scheme := runtime.NewScheme() 840 err := configapi.AddToScheme(test_scheme) 841 if err != nil { 842 t.Fatal(err) 843 } 844 845 defaultConfig := &configapi.Configuration{} 846 test_scheme.Default(defaultConfig) 847 848 testcases := []struct { 849 name string 850 scheme *runtime.Scheme 851 cfg *configapi.Configuration 852 wantResult map[string]any 853 }{ 854 855 { 856 name: "empty", 857 scheme: test_scheme, 858 cfg: &configapi.Configuration{}, 859 wantResult: map[string]any{ 860 "apiVersion": "config.kueue.x-k8s.io/v1beta1", 861 "kind": "Configuration", 862 "manageJobsWithoutQueueName": false, 863 "health": map[string]any{}, 864 "metrics": map[string]any{}, 865 "webhook": map[string]any{}, 866 }, 867 }, 868 { 869 name: "default", 870 scheme: test_scheme, 871 cfg: defaultConfig, 872 wantResult: map[string]any{ 873 "apiVersion": "config.kueue.x-k8s.io/v1beta1", 874 "kind": "Configuration", 875 "namespace": configapi.DefaultNamespace, 876 "webhook": map[string]any{ 877 "port": int64(configapi.DefaultWebhookPort), 878 }, 879 "metrics": map[string]any{ 880 "bindAddress": configapi.DefaultMetricsBindAddress, 881 }, 882 "health": map[string]any{ 883 "healthProbeBindAddress": configapi.DefaultHealthProbeBindAddress, 884 }, 885 "leaderElection": map[string]any{ 886 "leaderElect": true, 887 "leaseDuration": configapi.DefaultLeaderElectionLeaseDuration.String(), 888 "renewDeadline": configapi.DefaultLeaderElectionRenewDeadline.String(), 889 "retryPeriod": configapi.DefaultLeaderElectionRetryPeriod.String(), 890 "resourceLock": resourcelock.LeasesResourceLock, 891 "resourceName": configapi.DefaultLeaderElectionID, 892 "resourceNamespace": "", 893 }, 894 "internalCertManagement": map[string]any{ 895 "enable": true, 896 "webhookServiceName": configapi.DefaultWebhookServiceName, 897 "webhookSecretName": configapi.DefaultWebhookSecretName, 898 }, 899 "clientConnection": map[string]any{ 900 "burst": int64(configapi.DefaultClientConnectionBurst), 901 "qps": int64(configapi.DefaultClientConnectionQPS), 902 }, 903 "manageJobsWithoutQueueName": false, 904 "integrations": map[string]any{ 905 "frameworks": []any{"batch/job"}, 906 "podOptions": map[string]any{ 907 "namespaceSelector": map[string]any{ 908 "matchExpressions": []any{map[string]any{ 909 "key": "kubernetes.io/metadata.name", 910 "operator": "NotIn", 911 "values": []any{"kube-system", "kueue-system"}, 912 }}, 913 }, 914 "podSelector": map[string]any{}, 915 }, 916 }, 917 "queueVisibility": map[string]any{ 918 "updateIntervalSeconds": int64(configapi.DefaultQueueVisibilityUpdateIntervalSeconds), 919 "clusterQueues": map[string]any{"maxCount": int64(10)}, 920 }, 921 "multiKueue": map[string]any{ 922 "gcInterval": "1m0s", 923 "origin": "multikueue", 924 }, 925 }, 926 }, 927 } 928 for _, tc := range testcases { 929 t.Run(tc.name, func(t *testing.T) { 930 got, err := Encode(tc.scheme, tc.cfg) 931 if err != nil { 932 t.Errorf("Unexpected error:%s", err) 933 } 934 gotMap := map[string]interface{}{} 935 err = yaml.Unmarshal([]byte(got), &gotMap) 936 if err != nil { 937 t.Errorf("Unable to unmarshal result:%s", err) 938 } 939 if diff := cmp.Diff(tc.wantResult, gotMap); diff != "" { 940 t.Errorf("Unexpected result (-want +got):\n%s", diff) 941 } 942 }) 943 } 944 } 945 946 func TestWaitForPodsReadyIsEnabled(t *testing.T) { 947 cases := map[string]struct { 948 cfg *configapi.Configuration 949 want bool 950 }{ 951 "cfg.waitForPodsReady is null": { 952 cfg: &configapi.Configuration{}, 953 }, 954 "cfg.WaitForPodsReadyIsEnabled.enable is false": { 955 cfg: &configapi.Configuration{ 956 WaitForPodsReady: &configapi.WaitForPodsReady{}, 957 }, 958 }, 959 "waitForPodsReady is true": { 960 cfg: &configapi.Configuration{ 961 WaitForPodsReady: &configapi.WaitForPodsReady{ 962 Enable: true, 963 }, 964 }, 965 want: true, 966 }, 967 } 968 for name, tc := range cases { 969 t.Run(name, func(t *testing.T) { 970 got := WaitForPodsReadyIsEnabled(tc.cfg) 971 if tc.want != got { 972 t.Errorf("Unexpected result from WaitForPodsReadyIsEnabled\nwant:\n%v\ngot:%v\n", tc.want, got) 973 } 974 }) 975 } 976 }