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  }