...

Source file src/k8s.io/kubernetes/cmd/kube-controller-manager/app/options/options_test.go

Documentation: k8s.io/kubernetes/cmd/kube-controller-manager/app/options

     1  /*
     2  Copyright 2017 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 options
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"sort"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/spf13/pflag"
    29  
    30  	eventv1 "k8s.io/api/events/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    33  	apiserveroptions "k8s.io/apiserver/pkg/server/options"
    34  	cpconfig "k8s.io/cloud-provider/config"
    35  	serviceconfig "k8s.io/cloud-provider/controllers/service/config"
    36  	cpoptions "k8s.io/cloud-provider/options"
    37  	componentbaseconfig "k8s.io/component-base/config"
    38  	"k8s.io/component-base/logs"
    39  	"k8s.io/component-base/metrics"
    40  	cmconfig "k8s.io/controller-manager/config"
    41  	cmoptions "k8s.io/controller-manager/options"
    42  	migration "k8s.io/controller-manager/pkg/leadermigration/options"
    43  	netutils "k8s.io/utils/net"
    44  
    45  	clientgofeaturegate "k8s.io/client-go/features"
    46  	kubecontrollerconfig "k8s.io/kubernetes/cmd/kube-controller-manager/app/config"
    47  	kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config"
    48  	csrsigningconfig "k8s.io/kubernetes/pkg/controller/certificates/signer/config"
    49  	cronjobconfig "k8s.io/kubernetes/pkg/controller/cronjob/config"
    50  	daemonconfig "k8s.io/kubernetes/pkg/controller/daemon/config"
    51  	deploymentconfig "k8s.io/kubernetes/pkg/controller/deployment/config"
    52  	endpointconfig "k8s.io/kubernetes/pkg/controller/endpoint/config"
    53  	endpointsliceconfig "k8s.io/kubernetes/pkg/controller/endpointslice/config"
    54  	endpointslicemirroringconfig "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config"
    55  	garbagecollectorconfig "k8s.io/kubernetes/pkg/controller/garbagecollector/config"
    56  	jobconfig "k8s.io/kubernetes/pkg/controller/job/config"
    57  	namespaceconfig "k8s.io/kubernetes/pkg/controller/namespace/config"
    58  	nodeipamconfig "k8s.io/kubernetes/pkg/controller/nodeipam/config"
    59  	nodelifecycleconfig "k8s.io/kubernetes/pkg/controller/nodelifecycle/config"
    60  	poautosclerconfig "k8s.io/kubernetes/pkg/controller/podautoscaler/config"
    61  	podgcconfig "k8s.io/kubernetes/pkg/controller/podgc/config"
    62  	replicasetconfig "k8s.io/kubernetes/pkg/controller/replicaset/config"
    63  	replicationconfig "k8s.io/kubernetes/pkg/controller/replication/config"
    64  	resourcequotaconfig "k8s.io/kubernetes/pkg/controller/resourcequota/config"
    65  	serviceaccountconfig "k8s.io/kubernetes/pkg/controller/serviceaccount/config"
    66  	statefulsetconfig "k8s.io/kubernetes/pkg/controller/statefulset/config"
    67  	ttlafterfinishedconfig "k8s.io/kubernetes/pkg/controller/ttlafterfinished/config"
    68  	validatingadmissionpolicystatusconfig "k8s.io/kubernetes/pkg/controller/validatingadmissionpolicystatus/config"
    69  	attachdetachconfig "k8s.io/kubernetes/pkg/controller/volume/attachdetach/config"
    70  	ephemeralvolumeconfig "k8s.io/kubernetes/pkg/controller/volume/ephemeral/config"
    71  	persistentvolumeconfig "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/config"
    72  )
    73  
    74  var args = []string{
    75  	"--allocate-node-cidrs=true",
    76  	"--attach-detach-reconcile-sync-period=30s",
    77  	"--cidr-allocator-type=CloudAllocator",
    78  	"--cloud-config=/cloud-config",
    79  	"--cloud-provider=gce",
    80  	"--cluster-cidr=1.2.3.4/24",
    81  	"--cluster-name=k8s",
    82  	"--cluster-signing-cert-file=/cluster-signing-cert",
    83  	"--cluster-signing-key-file=/cluster-signing-key",
    84  	"--cluster-signing-kubelet-serving-cert-file=/cluster-signing-kubelet-serving/cert-file",
    85  	"--cluster-signing-kubelet-serving-key-file=/cluster-signing-kubelet-serving/key-file",
    86  	"--cluster-signing-kubelet-client-cert-file=/cluster-signing-kubelet-client/cert-file",
    87  	"--cluster-signing-kubelet-client-key-file=/cluster-signing-kubelet-client/key-file",
    88  	"--cluster-signing-kube-apiserver-client-cert-file=/cluster-signing-kube-apiserver-client/cert-file",
    89  	"--cluster-signing-kube-apiserver-client-key-file=/cluster-signing-kube-apiserver-client/key-file",
    90  	"--cluster-signing-legacy-unknown-cert-file=/cluster-signing-legacy-unknown/cert-file",
    91  	"--cluster-signing-legacy-unknown-key-file=/cluster-signing-legacy-unknown/key-file",
    92  	"--concurrent-deployment-syncs=10",
    93  	"--concurrent-horizontal-pod-autoscaler-syncs=10",
    94  	"--concurrent-statefulset-syncs=15",
    95  	"--concurrent-endpoint-syncs=10",
    96  	"--concurrent-ephemeralvolume-syncs=10",
    97  	"--concurrent-service-endpoint-syncs=10",
    98  	"--concurrent-gc-syncs=30",
    99  	"--concurrent-namespace-syncs=20",
   100  	"--concurrent-job-syncs=10",
   101  	"--concurrent-cron-job-syncs=10",
   102  	"--concurrent-replicaset-syncs=10",
   103  	"--concurrent-resource-quota-syncs=10",
   104  	"--concurrent-service-syncs=2",
   105  	"--concurrent-serviceaccount-token-syncs=10",
   106  	"--concurrent_rc_syncs=10",
   107  	"--concurrent-validating-admission-policy-status-syncs=9",
   108  	"--configure-cloud-routes=false",
   109  	"--contention-profiling=true",
   110  	"--controller-start-interval=2m",
   111  	"--controllers=foo,bar",
   112  	"--disable-attach-detach-reconcile-sync=true",
   113  	"--enable-dynamic-provisioning=false",
   114  	"--enable-garbage-collector=false",
   115  	"--enable-hostpath-provisioner=true",
   116  	"--cluster-signing-duration=10h",
   117  	"--flex-volume-plugin-dir=/flex-volume-plugin",
   118  	"--volume-host-cidr-denylist=127.0.0.1/28,feed::/16",
   119  	"--volume-host-allow-local-loopback=false",
   120  	"--horizontal-pod-autoscaler-downscale-delay=2m",
   121  	"--horizontal-pod-autoscaler-sync-period=45s",
   122  	"--horizontal-pod-autoscaler-upscale-delay=1m",
   123  	"--horizontal-pod-autoscaler-downscale-stabilization=3m",
   124  	"--horizontal-pod-autoscaler-cpu-initialization-period=90s",
   125  	"--horizontal-pod-autoscaler-initial-readiness-delay=50s",
   126  	"--http2-max-streams-per-connection=47",
   127  	"--kube-api-burst=100",
   128  	"--kube-api-content-type=application/json",
   129  	"--kube-api-qps=50.0",
   130  	"--kubeconfig=/kubeconfig",
   131  	"--large-cluster-size-threshold=100",
   132  	"--leader-elect=false",
   133  	"--leader-elect-lease-duration=30s",
   134  	"--leader-elect-renew-deadline=15s",
   135  	"--leader-elect-resource-lock=configmap",
   136  	"--leader-elect-retry-period=5s",
   137  	"--legacy-service-account-token-clean-up-period=8760h",
   138  	"--master=192.168.4.20",
   139  	"--max-endpoints-per-slice=200",
   140  	"--min-resync-period=8h",
   141  	"--mirroring-concurrent-service-endpoint-syncs=2",
   142  	"--mirroring-max-endpoints-per-subset=1000",
   143  	"--namespace-sync-period=10m",
   144  	"--node-cidr-mask-size=48",
   145  	"--node-cidr-mask-size-ipv4=48",
   146  	"--node-cidr-mask-size-ipv6=108",
   147  	"--node-eviction-rate=0.2",
   148  	"--node-monitor-grace-period=30s",
   149  	"--node-monitor-period=10s",
   150  	"--node-startup-grace-period=30s",
   151  	"--profiling=false",
   152  	"--pv-recycler-increment-timeout-nfs=45",
   153  	"--pv-recycler-minimum-timeout-hostpath=45",
   154  	"--pv-recycler-minimum-timeout-nfs=200",
   155  	"--pv-recycler-timeout-increment-hostpath=45",
   156  	"--pvclaimbinder-sync-period=30s",
   157  	"--resource-quota-sync-period=10m",
   158  	"--route-reconciliation-period=30s",
   159  	"--secondary-node-eviction-rate=0.05",
   160  	"--service-account-private-key-file=/service-account-private-key",
   161  	"--terminated-pod-gc-threshold=12000",
   162  	"--unhealthy-zone-threshold=0.6",
   163  	"--use-service-account-credentials=true",
   164  	"--cert-dir=/a/b/c",
   165  	"--bind-address=192.168.4.21",
   166  	"--secure-port=10001",
   167  	"--concurrent-ttl-after-finished-syncs=8",
   168  }
   169  
   170  func TestAddFlags(t *testing.T) {
   171  	fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
   172  	s, _ := NewKubeControllerManagerOptions()
   173  	for _, f := range s.Flags([]string{""}, []string{""}, nil).FlagSets {
   174  		fs.AddFlagSet(f)
   175  	}
   176  
   177  	fs.Parse(args)
   178  	// Sort GCIgnoredResources because it's built from a map, which means the
   179  	// insertion order is random.
   180  	sort.Sort(sortedGCIgnoredResources(s.GarbageCollectorController.GCIgnoredResources))
   181  
   182  	expected := &KubeControllerManagerOptions{
   183  		Generic: &cmoptions.GenericControllerManagerConfigurationOptions{
   184  			GenericControllerManagerConfiguration: &cmconfig.GenericControllerManagerConfiguration{
   185  				Address:         "0.0.0.0", // Note: This field should have no effect in CM now, and "0.0.0.0" is the default value.
   186  				MinResyncPeriod: metav1.Duration{Duration: 8 * time.Hour},
   187  				ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
   188  					Kubeconfig:  "/kubeconfig",
   189  					ContentType: "application/json",
   190  					QPS:         50.0,
   191  					Burst:       100,
   192  				},
   193  				ControllerStartInterval: metav1.Duration{Duration: 2 * time.Minute},
   194  				LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
   195  					ResourceLock:      "configmap",
   196  					LeaderElect:       false,
   197  					LeaseDuration:     metav1.Duration{Duration: 30 * time.Second},
   198  					RenewDeadline:     metav1.Duration{Duration: 15 * time.Second},
   199  					RetryPeriod:       metav1.Duration{Duration: 5 * time.Second},
   200  					ResourceName:      "kube-controller-manager",
   201  					ResourceNamespace: "kube-system",
   202  				},
   203  				Controllers: []string{"foo", "bar"},
   204  			},
   205  			Debugging: &cmoptions.DebuggingOptions{
   206  				DebuggingConfiguration: &componentbaseconfig.DebuggingConfiguration{
   207  					EnableProfiling:           false,
   208  					EnableContentionProfiling: true,
   209  				},
   210  			},
   211  			LeaderMigration: &migration.LeaderMigrationOptions{},
   212  		},
   213  		KubeCloudShared: &cpoptions.KubeCloudSharedOptions{
   214  			KubeCloudSharedConfiguration: &cpconfig.KubeCloudSharedConfiguration{
   215  				UseServiceAccountCredentials: true,
   216  				RouteReconciliationPeriod:    metav1.Duration{Duration: 30 * time.Second},
   217  				NodeMonitorPeriod:            metav1.Duration{Duration: 10 * time.Second},
   218  				ClusterName:                  "k8s",
   219  				ClusterCIDR:                  "1.2.3.4/24",
   220  				AllocateNodeCIDRs:            true,
   221  				CIDRAllocatorType:            "CloudAllocator",
   222  				ConfigureCloudRoutes:         false,
   223  			},
   224  			CloudProvider: &cpoptions.CloudProviderOptions{
   225  				CloudProviderConfiguration: &cpconfig.CloudProviderConfiguration{
   226  					Name:            "gce",
   227  					CloudConfigFile: "/cloud-config",
   228  				},
   229  			},
   230  		},
   231  		ServiceController: &cpoptions.ServiceControllerOptions{
   232  			ServiceControllerConfiguration: &serviceconfig.ServiceControllerConfiguration{
   233  				ConcurrentServiceSyncs: 2,
   234  			},
   235  		},
   236  		AttachDetachController: &AttachDetachControllerOptions{
   237  			&attachdetachconfig.AttachDetachControllerConfiguration{
   238  				ReconcilerSyncLoopPeriod:          metav1.Duration{Duration: 30 * time.Second},
   239  				DisableAttachDetachReconcilerSync: true,
   240  			},
   241  		},
   242  		CSRSigningController: &CSRSigningControllerOptions{
   243  			&csrsigningconfig.CSRSigningControllerConfiguration{
   244  				ClusterSigningCertFile: "/cluster-signing-cert",
   245  				ClusterSigningKeyFile:  "/cluster-signing-key",
   246  				ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   247  				KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   248  					CertFile: "/cluster-signing-kubelet-serving/cert-file",
   249  					KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   250  				},
   251  				KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   252  					CertFile: "/cluster-signing-kubelet-client/cert-file",
   253  					KeyFile:  "/cluster-signing-kubelet-client/key-file",
   254  				},
   255  				KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   256  					CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   257  					KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   258  				},
   259  				LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   260  					CertFile: "/cluster-signing-legacy-unknown/cert-file",
   261  					KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   262  				},
   263  			},
   264  		},
   265  		DaemonSetController: &DaemonSetControllerOptions{
   266  			&daemonconfig.DaemonSetControllerConfiguration{
   267  				ConcurrentDaemonSetSyncs: 2,
   268  			},
   269  		},
   270  		DeploymentController: &DeploymentControllerOptions{
   271  			&deploymentconfig.DeploymentControllerConfiguration{
   272  				ConcurrentDeploymentSyncs: 10,
   273  			},
   274  		},
   275  		StatefulSetController: &StatefulSetControllerOptions{
   276  			&statefulsetconfig.StatefulSetControllerConfiguration{
   277  				ConcurrentStatefulSetSyncs: 15,
   278  			},
   279  		},
   280  		DeprecatedFlags: &DeprecatedControllerOptions{
   281  			&kubectrlmgrconfig.DeprecatedControllerConfiguration{},
   282  		},
   283  		EndpointController: &EndpointControllerOptions{
   284  			&endpointconfig.EndpointControllerConfiguration{
   285  				ConcurrentEndpointSyncs: 10,
   286  			},
   287  		},
   288  		EndpointSliceController: &EndpointSliceControllerOptions{
   289  			&endpointsliceconfig.EndpointSliceControllerConfiguration{
   290  				ConcurrentServiceEndpointSyncs: 10,
   291  				MaxEndpointsPerSlice:           200,
   292  			},
   293  		},
   294  		EndpointSliceMirroringController: &EndpointSliceMirroringControllerOptions{
   295  			&endpointslicemirroringconfig.EndpointSliceMirroringControllerConfiguration{
   296  				MirroringConcurrentServiceEndpointSyncs: 2,
   297  				MirroringMaxEndpointsPerSubset:          1000,
   298  			},
   299  		},
   300  		EphemeralVolumeController: &EphemeralVolumeControllerOptions{
   301  			&ephemeralvolumeconfig.EphemeralVolumeControllerConfiguration{
   302  				ConcurrentEphemeralVolumeSyncs: 10,
   303  			},
   304  		},
   305  		GarbageCollectorController: &GarbageCollectorControllerOptions{
   306  			&garbagecollectorconfig.GarbageCollectorControllerConfiguration{
   307  				ConcurrentGCSyncs: 30,
   308  				GCIgnoredResources: []garbagecollectorconfig.GroupResource{
   309  					{Group: "", Resource: "events"},
   310  					{Group: eventv1.GroupName, Resource: "events"},
   311  				},
   312  				EnableGarbageCollector: false,
   313  			},
   314  		},
   315  		HPAController: &HPAControllerOptions{
   316  			&poautosclerconfig.HPAControllerConfiguration{
   317  				ConcurrentHorizontalPodAutoscalerSyncs:              10,
   318  				HorizontalPodAutoscalerSyncPeriod:                   metav1.Duration{Duration: 45 * time.Second},
   319  				HorizontalPodAutoscalerUpscaleForbiddenWindow:       metav1.Duration{Duration: 1 * time.Minute},
   320  				HorizontalPodAutoscalerDownscaleForbiddenWindow:     metav1.Duration{Duration: 2 * time.Minute},
   321  				HorizontalPodAutoscalerDownscaleStabilizationWindow: metav1.Duration{Duration: 3 * time.Minute},
   322  				HorizontalPodAutoscalerCPUInitializationPeriod:      metav1.Duration{Duration: 90 * time.Second},
   323  				HorizontalPodAutoscalerInitialReadinessDelay:        metav1.Duration{Duration: 50 * time.Second},
   324  				HorizontalPodAutoscalerTolerance:                    0.1,
   325  			},
   326  		},
   327  		JobController: &JobControllerOptions{
   328  			&jobconfig.JobControllerConfiguration{
   329  				ConcurrentJobSyncs: 10,
   330  			},
   331  		},
   332  		CronJobController: &CronJobControllerOptions{
   333  			&cronjobconfig.CronJobControllerConfiguration{
   334  				ConcurrentCronJobSyncs: 10,
   335  			},
   336  		},
   337  		NamespaceController: &NamespaceControllerOptions{
   338  			&namespaceconfig.NamespaceControllerConfiguration{
   339  				NamespaceSyncPeriod:      metav1.Duration{Duration: 10 * time.Minute},
   340  				ConcurrentNamespaceSyncs: 20,
   341  			},
   342  		},
   343  		NodeIPAMController: &NodeIPAMControllerOptions{
   344  			&nodeipamconfig.NodeIPAMControllerConfiguration{
   345  				NodeCIDRMaskSize:     48,
   346  				NodeCIDRMaskSizeIPv4: 48,
   347  				NodeCIDRMaskSizeIPv6: 108,
   348  			},
   349  		},
   350  		NodeLifecycleController: &NodeLifecycleControllerOptions{
   351  			&nodelifecycleconfig.NodeLifecycleControllerConfiguration{
   352  				NodeEvictionRate:          0.2,
   353  				SecondaryNodeEvictionRate: 0.05,
   354  				NodeMonitorGracePeriod:    metav1.Duration{Duration: 30 * time.Second},
   355  				NodeStartupGracePeriod:    metav1.Duration{Duration: 30 * time.Second},
   356  				LargeClusterSizeThreshold: 100,
   357  				UnhealthyZoneThreshold:    0.6,
   358  			},
   359  		},
   360  		PersistentVolumeBinderController: &PersistentVolumeBinderControllerOptions{
   361  			&persistentvolumeconfig.PersistentVolumeBinderControllerConfiguration{
   362  				PVClaimBinderSyncPeriod: metav1.Duration{Duration: 30 * time.Second},
   363  				VolumeConfiguration: persistentvolumeconfig.VolumeConfiguration{
   364  					EnableDynamicProvisioning:  false,
   365  					EnableHostPathProvisioning: true,
   366  					FlexVolumePluginDir:        "/flex-volume-plugin",
   367  					PersistentVolumeRecyclerConfiguration: persistentvolumeconfig.PersistentVolumeRecyclerConfiguration{
   368  						MaximumRetry:             3,
   369  						MinimumTimeoutNFS:        200,
   370  						IncrementTimeoutNFS:      45,
   371  						MinimumTimeoutHostPath:   45,
   372  						IncrementTimeoutHostPath: 45,
   373  					},
   374  				},
   375  				VolumeHostCIDRDenylist:       []string{"127.0.0.1/28", "feed::/16"},
   376  				VolumeHostAllowLocalLoopback: false,
   377  			},
   378  		},
   379  		PodGCController: &PodGCControllerOptions{
   380  			&podgcconfig.PodGCControllerConfiguration{
   381  				TerminatedPodGCThreshold: 12000,
   382  			},
   383  		},
   384  		ReplicaSetController: &ReplicaSetControllerOptions{
   385  			&replicasetconfig.ReplicaSetControllerConfiguration{
   386  				ConcurrentRSSyncs: 10,
   387  			},
   388  		},
   389  		ReplicationController: &ReplicationControllerOptions{
   390  			&replicationconfig.ReplicationControllerConfiguration{
   391  				ConcurrentRCSyncs: 10,
   392  			},
   393  		},
   394  		ResourceQuotaController: &ResourceQuotaControllerOptions{
   395  			&resourcequotaconfig.ResourceQuotaControllerConfiguration{
   396  				ResourceQuotaSyncPeriod:      metav1.Duration{Duration: 10 * time.Minute},
   397  				ConcurrentResourceQuotaSyncs: 10,
   398  			},
   399  		},
   400  		SAController: &SAControllerOptions{
   401  			&serviceaccountconfig.SAControllerConfiguration{
   402  				ServiceAccountKeyFile:  "/service-account-private-key",
   403  				ConcurrentSATokenSyncs: 10,
   404  			},
   405  		},
   406  		LegacySATokenCleaner: &LegacySATokenCleanerOptions{
   407  			&serviceaccountconfig.LegacySATokenCleanerConfiguration{
   408  				CleanUpPeriod: metav1.Duration{Duration: 365 * 24 * time.Hour},
   409  			},
   410  		},
   411  		TTLAfterFinishedController: &TTLAfterFinishedControllerOptions{
   412  			&ttlafterfinishedconfig.TTLAfterFinishedControllerConfiguration{
   413  				ConcurrentTTLSyncs: 8,
   414  			},
   415  		},
   416  		ValidatingAdmissionPolicyStatusController: &ValidatingAdmissionPolicyStatusControllerOptions{
   417  			&validatingadmissionpolicystatusconfig.ValidatingAdmissionPolicyStatusControllerConfiguration{
   418  				ConcurrentPolicySyncs: 9,
   419  			},
   420  		},
   421  		SecureServing: (&apiserveroptions.SecureServingOptions{
   422  			BindPort:    10001,
   423  			BindAddress: netutils.ParseIPSloppy("192.168.4.21"),
   424  			ServerCert: apiserveroptions.GeneratableKeyCert{
   425  				CertDirectory: "/a/b/c",
   426  				PairName:      "kube-controller-manager",
   427  			},
   428  			HTTP2MaxStreamsPerConnection: 47,
   429  		}).WithLoopback(),
   430  		Authentication: &apiserveroptions.DelegatingAuthenticationOptions{
   431  			CacheTTL:            10 * time.Second,
   432  			TokenRequestTimeout: 10 * time.Second,
   433  			WebhookRetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
   434  			ClientCert:          apiserveroptions.ClientCertAuthenticationOptions{},
   435  			RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
   436  				UsernameHeaders:     []string{"x-remote-user"},
   437  				GroupHeaders:        []string{"x-remote-group"},
   438  				ExtraHeaderPrefixes: []string{"x-remote-extra-"},
   439  			},
   440  			RemoteKubeConfigFileOptional: true,
   441  		},
   442  		Authorization: &apiserveroptions.DelegatingAuthorizationOptions{
   443  			AllowCacheTTL:                10 * time.Second,
   444  			DenyCacheTTL:                 10 * time.Second,
   445  			ClientTimeout:                10 * time.Second,
   446  			WebhookRetryBackoff:          apiserveroptions.DefaultAuthWebhookRetryBackoff(),
   447  			RemoteKubeConfigFileOptional: true,
   448  			AlwaysAllowPaths:             []string{"/healthz", "/readyz", "/livez"}, // note: this does not match /healthz/ or /healthz/*
   449  			AlwaysAllowGroups:            []string{"system:masters"},
   450  		},
   451  		Master:  "192.168.4.20",
   452  		Metrics: &metrics.Options{},
   453  		Logs:    logs.NewOptions(),
   454  	}
   455  
   456  	// Sort GCIgnoredResources because it's built from a map, which means the
   457  	// insertion order is random.
   458  	sort.Sort(sortedGCIgnoredResources(expected.GarbageCollectorController.GCIgnoredResources))
   459  
   460  	if !reflect.DeepEqual(expected, s) {
   461  		t.Errorf("Got different run options than expected.\nDifference detected on:\n%s", cmp.Diff(expected, s))
   462  	}
   463  }
   464  
   465  func TestApplyTo(t *testing.T) {
   466  	fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
   467  	s, _ := NewKubeControllerManagerOptions()
   468  	// flag set to parse the args that are required to start the kube controller manager
   469  	for _, f := range s.Flags([]string{""}, []string{""}, nil).FlagSets {
   470  		fs.AddFlagSet(f)
   471  	}
   472  
   473  	fs.Parse(args)
   474  	// Sort GCIgnoredResources because it's built from a map, which means the
   475  	// insertion order is random.
   476  	sort.Sort(sortedGCIgnoredResources(s.GarbageCollectorController.GCIgnoredResources))
   477  
   478  	expected := &kubecontrollerconfig.Config{
   479  		ComponentConfig: kubectrlmgrconfig.KubeControllerManagerConfiguration{
   480  			Generic: cmconfig.GenericControllerManagerConfiguration{
   481  				Address:         "0.0.0.0", // Note: This field should have no effect in CM now, and "0.0.0.0" is the default value.
   482  				MinResyncPeriod: metav1.Duration{Duration: 8 * time.Hour},
   483  				ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
   484  					Kubeconfig:  "/kubeconfig",
   485  					ContentType: "application/json",
   486  					QPS:         50.0,
   487  					Burst:       100,
   488  				},
   489  				ControllerStartInterval: metav1.Duration{Duration: 2 * time.Minute},
   490  				LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
   491  					ResourceLock:      "configmap",
   492  					LeaderElect:       false,
   493  					LeaseDuration:     metav1.Duration{Duration: 30 * time.Second},
   494  					RenewDeadline:     metav1.Duration{Duration: 15 * time.Second},
   495  					RetryPeriod:       metav1.Duration{Duration: 5 * time.Second},
   496  					ResourceName:      "kube-controller-manager",
   497  					ResourceNamespace: "kube-system",
   498  				},
   499  				Controllers: []string{"foo", "bar"},
   500  				Debugging: componentbaseconfig.DebuggingConfiguration{
   501  					EnableProfiling:           false,
   502  					EnableContentionProfiling: true,
   503  				},
   504  			},
   505  			KubeCloudShared: cpconfig.KubeCloudSharedConfiguration{
   506  				UseServiceAccountCredentials: true,
   507  				RouteReconciliationPeriod:    metav1.Duration{Duration: 30 * time.Second},
   508  				NodeMonitorPeriod:            metav1.Duration{Duration: 10 * time.Second},
   509  				ClusterName:                  "k8s",
   510  				ClusterCIDR:                  "1.2.3.4/24",
   511  				AllocateNodeCIDRs:            true,
   512  				CIDRAllocatorType:            "CloudAllocator",
   513  				ConfigureCloudRoutes:         false,
   514  				CloudProvider: cpconfig.CloudProviderConfiguration{
   515  					Name:            "gce",
   516  					CloudConfigFile: "/cloud-config",
   517  				},
   518  			},
   519  			ServiceController: serviceconfig.ServiceControllerConfiguration{
   520  				ConcurrentServiceSyncs: 2,
   521  			},
   522  			AttachDetachController: attachdetachconfig.AttachDetachControllerConfiguration{
   523  				ReconcilerSyncLoopPeriod:          metav1.Duration{Duration: 30 * time.Second},
   524  				DisableAttachDetachReconcilerSync: true,
   525  			},
   526  			CSRSigningController: csrsigningconfig.CSRSigningControllerConfiguration{
   527  				ClusterSigningCertFile: "/cluster-signing-cert",
   528  				ClusterSigningKeyFile:  "/cluster-signing-key",
   529  				ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   530  				KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   531  					CertFile: "/cluster-signing-kubelet-serving/cert-file",
   532  					KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   533  				},
   534  				KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   535  					CertFile: "/cluster-signing-kubelet-client/cert-file",
   536  					KeyFile:  "/cluster-signing-kubelet-client/key-file",
   537  				},
   538  				KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   539  					CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   540  					KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   541  				},
   542  				LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   543  					CertFile: "/cluster-signing-legacy-unknown/cert-file",
   544  					KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   545  				},
   546  			},
   547  			DaemonSetController: daemonconfig.DaemonSetControllerConfiguration{
   548  				ConcurrentDaemonSetSyncs: 2,
   549  			},
   550  			DeploymentController: deploymentconfig.DeploymentControllerConfiguration{
   551  				ConcurrentDeploymentSyncs: 10,
   552  			},
   553  			StatefulSetController: statefulsetconfig.StatefulSetControllerConfiguration{
   554  				ConcurrentStatefulSetSyncs: 15,
   555  			},
   556  			DeprecatedController: kubectrlmgrconfig.DeprecatedControllerConfiguration{},
   557  			EndpointController: endpointconfig.EndpointControllerConfiguration{
   558  				ConcurrentEndpointSyncs: 10,
   559  			},
   560  			EndpointSliceController: endpointsliceconfig.EndpointSliceControllerConfiguration{
   561  				ConcurrentServiceEndpointSyncs: 10,
   562  				MaxEndpointsPerSlice:           200,
   563  			},
   564  			EndpointSliceMirroringController: endpointslicemirroringconfig.EndpointSliceMirroringControllerConfiguration{
   565  				MirroringConcurrentServiceEndpointSyncs: 2,
   566  				MirroringMaxEndpointsPerSubset:          1000,
   567  			},
   568  			EphemeralVolumeController: ephemeralvolumeconfig.EphemeralVolumeControllerConfiguration{
   569  				ConcurrentEphemeralVolumeSyncs: 10,
   570  			},
   571  			GarbageCollectorController: garbagecollectorconfig.GarbageCollectorControllerConfiguration{
   572  				ConcurrentGCSyncs: 30,
   573  				GCIgnoredResources: []garbagecollectorconfig.GroupResource{
   574  					{Group: "", Resource: "events"},
   575  					{Group: eventv1.GroupName, Resource: "events"},
   576  				},
   577  				EnableGarbageCollector: false,
   578  			},
   579  			HPAController: poautosclerconfig.HPAControllerConfiguration{
   580  				ConcurrentHorizontalPodAutoscalerSyncs:              10,
   581  				HorizontalPodAutoscalerSyncPeriod:                   metav1.Duration{Duration: 45 * time.Second},
   582  				HorizontalPodAutoscalerUpscaleForbiddenWindow:       metav1.Duration{Duration: 1 * time.Minute},
   583  				HorizontalPodAutoscalerDownscaleForbiddenWindow:     metav1.Duration{Duration: 2 * time.Minute},
   584  				HorizontalPodAutoscalerDownscaleStabilizationWindow: metav1.Duration{Duration: 3 * time.Minute},
   585  				HorizontalPodAutoscalerCPUInitializationPeriod:      metav1.Duration{Duration: 90 * time.Second},
   586  				HorizontalPodAutoscalerInitialReadinessDelay:        metav1.Duration{Duration: 50 * time.Second},
   587  				HorizontalPodAutoscalerTolerance:                    0.1,
   588  			},
   589  			JobController: jobconfig.JobControllerConfiguration{
   590  				ConcurrentJobSyncs: 10,
   591  			},
   592  			CronJobController: cronjobconfig.CronJobControllerConfiguration{
   593  				ConcurrentCronJobSyncs: 10,
   594  			},
   595  			NamespaceController: namespaceconfig.NamespaceControllerConfiguration{
   596  				NamespaceSyncPeriod:      metav1.Duration{Duration: 10 * time.Minute},
   597  				ConcurrentNamespaceSyncs: 20,
   598  			},
   599  			NodeIPAMController: nodeipamconfig.NodeIPAMControllerConfiguration{
   600  				NodeCIDRMaskSize:     48,
   601  				NodeCIDRMaskSizeIPv4: 48,
   602  				NodeCIDRMaskSizeIPv6: 108,
   603  			},
   604  			NodeLifecycleController: nodelifecycleconfig.NodeLifecycleControllerConfiguration{
   605  				NodeEvictionRate:          0.2,
   606  				SecondaryNodeEvictionRate: 0.05,
   607  				NodeMonitorGracePeriod:    metav1.Duration{Duration: 30 * time.Second},
   608  				NodeStartupGracePeriod:    metav1.Duration{Duration: 30 * time.Second},
   609  				LargeClusterSizeThreshold: 100,
   610  				UnhealthyZoneThreshold:    0.6,
   611  			},
   612  			PersistentVolumeBinderController: persistentvolumeconfig.PersistentVolumeBinderControllerConfiguration{
   613  				PVClaimBinderSyncPeriod: metav1.Duration{Duration: 30 * time.Second},
   614  				VolumeConfiguration: persistentvolumeconfig.VolumeConfiguration{
   615  					EnableDynamicProvisioning:  false,
   616  					EnableHostPathProvisioning: true,
   617  					FlexVolumePluginDir:        "/flex-volume-plugin",
   618  					PersistentVolumeRecyclerConfiguration: persistentvolumeconfig.PersistentVolumeRecyclerConfiguration{
   619  						MaximumRetry:             3,
   620  						MinimumTimeoutNFS:        200,
   621  						IncrementTimeoutNFS:      45,
   622  						MinimumTimeoutHostPath:   45,
   623  						IncrementTimeoutHostPath: 45,
   624  					},
   625  				},
   626  				VolumeHostCIDRDenylist:       []string{"127.0.0.1/28", "feed::/16"},
   627  				VolumeHostAllowLocalLoopback: false,
   628  			},
   629  			PodGCController: podgcconfig.PodGCControllerConfiguration{
   630  				TerminatedPodGCThreshold: 12000,
   631  			},
   632  			ReplicaSetController: replicasetconfig.ReplicaSetControllerConfiguration{
   633  				ConcurrentRSSyncs: 10,
   634  			},
   635  			ReplicationController: replicationconfig.ReplicationControllerConfiguration{
   636  				ConcurrentRCSyncs: 10,
   637  			},
   638  			ResourceQuotaController: resourcequotaconfig.ResourceQuotaControllerConfiguration{
   639  				ResourceQuotaSyncPeriod:      metav1.Duration{Duration: 10 * time.Minute},
   640  				ConcurrentResourceQuotaSyncs: 10,
   641  			},
   642  			SAController: serviceaccountconfig.SAControllerConfiguration{
   643  				ServiceAccountKeyFile:  "/service-account-private-key",
   644  				ConcurrentSATokenSyncs: 10,
   645  			},
   646  			LegacySATokenCleaner: serviceaccountconfig.LegacySATokenCleanerConfiguration{
   647  				CleanUpPeriod: metav1.Duration{Duration: 365 * 24 * time.Hour},
   648  			},
   649  			TTLAfterFinishedController: ttlafterfinishedconfig.TTLAfterFinishedControllerConfiguration{
   650  				ConcurrentTTLSyncs: 8,
   651  			},
   652  			ValidatingAdmissionPolicyStatusController: validatingadmissionpolicystatusconfig.ValidatingAdmissionPolicyStatusControllerConfiguration{
   653  				ConcurrentPolicySyncs: 9,
   654  			},
   655  		},
   656  	}
   657  
   658  	// Sort GCIgnoredResources because it's built from a map, which means the
   659  	// insertion order is random.
   660  	sort.Sort(sortedGCIgnoredResources(expected.ComponentConfig.GarbageCollectorController.GCIgnoredResources))
   661  
   662  	c := &kubecontrollerconfig.Config{}
   663  	s.ApplyTo(c, []string{""}, []string{""}, nil)
   664  
   665  	if !reflect.DeepEqual(expected.ComponentConfig, c.ComponentConfig) {
   666  		t.Errorf("Got different configuration than expected.\nDifference detected on:\n%s", cmp.Diff(expected.ComponentConfig, c.ComponentConfig))
   667  	}
   668  }
   669  
   670  func TestValidateControllersOptions(t *testing.T) {
   671  	testCases := []struct {
   672  		name                   string
   673  		expectErrors           bool
   674  		expectedErrorSubString string
   675  		options                interface {
   676  			Validate() []error
   677  		}
   678  	}{
   679  		{
   680  			name:                   "AttachDetachControllerOptions reconciler sync loop period less than one second",
   681  			expectErrors:           true,
   682  			expectedErrorSubString: "duration time must be greater than one second",
   683  			options: &AttachDetachControllerOptions{
   684  				&attachdetachconfig.AttachDetachControllerConfiguration{
   685  					ReconcilerSyncLoopPeriod:          metav1.Duration{Duration: time.Second / 2},
   686  					DisableAttachDetachReconcilerSync: true,
   687  				},
   688  			},
   689  		},
   690  		{
   691  			name:                   "CSRSigningControllerOptions KubeletServingSignerConfiguration no cert file",
   692  			expectErrors:           true,
   693  			expectedErrorSubString: "cannot specify key without cert",
   694  			options: &CSRSigningControllerOptions{
   695  				&csrsigningconfig.CSRSigningControllerConfiguration{
   696  					ClusterSigningCertFile: "",
   697  					ClusterSigningKeyFile:  "",
   698  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   699  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   700  						CertFile: "",
   701  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   702  					},
   703  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   704  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   705  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   706  					},
   707  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   708  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   709  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   710  					},
   711  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   712  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   713  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   714  					},
   715  				},
   716  			},
   717  		},
   718  		{
   719  			name:                   "CSRSigningControllerOptions KubeletServingSignerConfiguration no key file",
   720  			expectErrors:           true,
   721  			expectedErrorSubString: "cannot specify cert without key",
   722  			options: &CSRSigningControllerOptions{
   723  				&csrsigningconfig.CSRSigningControllerConfiguration{
   724  					ClusterSigningCertFile: "",
   725  					ClusterSigningKeyFile:  "",
   726  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   727  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   728  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   729  						KeyFile:  "",
   730  					},
   731  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   732  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   733  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   734  					},
   735  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   736  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   737  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   738  					},
   739  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   740  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   741  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   742  					},
   743  				},
   744  			},
   745  		},
   746  		{
   747  			name:                   "CSRSigningControllerOptions KubeletClientSignerConfiguration no cert file",
   748  			expectErrors:           true,
   749  			expectedErrorSubString: "cannot specify key without cert",
   750  			options: &CSRSigningControllerOptions{
   751  				&csrsigningconfig.CSRSigningControllerConfiguration{
   752  					ClusterSigningCertFile: "",
   753  					ClusterSigningKeyFile:  "",
   754  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   755  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   756  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   757  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   758  					},
   759  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   760  						CertFile: "",
   761  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   762  					},
   763  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   764  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   765  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   766  					},
   767  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   768  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   769  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   770  					},
   771  				},
   772  			},
   773  		},
   774  		{
   775  			name:                   "CSRSigningControllerOptions KubeletClientSignerConfiguration no key file",
   776  			expectErrors:           true,
   777  			expectedErrorSubString: "cannot specify cert without key",
   778  			options: &CSRSigningControllerOptions{
   779  				&csrsigningconfig.CSRSigningControllerConfiguration{
   780  					ClusterSigningCertFile: "",
   781  					ClusterSigningKeyFile:  "",
   782  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   783  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   784  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   785  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   786  					},
   787  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   788  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   789  						KeyFile:  "",
   790  					},
   791  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   792  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   793  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   794  					},
   795  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   796  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   797  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   798  					},
   799  				},
   800  			},
   801  		},
   802  		{
   803  			name:                   "CSRSigningControllerOptions KubeAPIServerClientSignerConfiguration no cert file",
   804  			expectErrors:           true,
   805  			expectedErrorSubString: "cannot specify key without cert",
   806  			options: &CSRSigningControllerOptions{
   807  				&csrsigningconfig.CSRSigningControllerConfiguration{
   808  					ClusterSigningCertFile: "",
   809  					ClusterSigningKeyFile:  "",
   810  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   811  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   812  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   813  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   814  					},
   815  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   816  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   817  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   818  					},
   819  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   820  						CertFile: "",
   821  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   822  					},
   823  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   824  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   825  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   826  					},
   827  				},
   828  			},
   829  		},
   830  		{
   831  			name:                   "CSRSigningControllerOptions KubeAPIServerClientSignerConfiguration no key file",
   832  			expectErrors:           true,
   833  			expectedErrorSubString: "cannot specify cert without key",
   834  			options: &CSRSigningControllerOptions{
   835  				&csrsigningconfig.CSRSigningControllerConfiguration{
   836  					ClusterSigningCertFile: "",
   837  					ClusterSigningKeyFile:  "",
   838  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   839  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   840  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   841  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   842  					},
   843  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   844  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   845  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   846  					},
   847  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   848  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   849  						KeyFile:  "",
   850  					},
   851  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   852  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   853  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   854  					},
   855  				},
   856  			},
   857  		},
   858  		{
   859  			name:                   "CSRSigningControllerOptions LegacyUnknownSignerConfiguration no cert file",
   860  			expectErrors:           true,
   861  			expectedErrorSubString: "cannot specify key without cert",
   862  			options: &CSRSigningControllerOptions{
   863  				&csrsigningconfig.CSRSigningControllerConfiguration{
   864  					ClusterSigningCertFile: "",
   865  					ClusterSigningKeyFile:  "",
   866  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   867  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   868  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   869  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   870  					},
   871  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   872  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   873  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   874  					},
   875  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   876  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   877  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   878  					},
   879  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   880  						CertFile: "",
   881  						KeyFile:  "/cluster-signing-legacy-unknown/key-file",
   882  					},
   883  				},
   884  			},
   885  		},
   886  		{
   887  			name:                   "CSRSigningControllerOptions LegacyUnknownSignerConfiguration no key file",
   888  			expectErrors:           true,
   889  			expectedErrorSubString: "cannot specify cert without key",
   890  			options: &CSRSigningControllerOptions{
   891  				&csrsigningconfig.CSRSigningControllerConfiguration{
   892  					ClusterSigningCertFile: "",
   893  					ClusterSigningKeyFile:  "",
   894  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   895  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   896  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   897  						KeyFile:  "/cluster-signing-kubelet-serving/key-file",
   898  					},
   899  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   900  						CertFile: "/cluster-signing-kubelet-client/cert-file",
   901  						KeyFile:  "/cluster-signing-kubelet-client/key-file",
   902  					},
   903  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   904  						CertFile: "/cluster-signing-kube-apiserver-client/cert-file",
   905  						KeyFile:  "/cluster-signing-kube-apiserver-client/key-file",
   906  					},
   907  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   908  						CertFile: "/cluster-signing-legacy-unknown/cert-file",
   909  						KeyFile:  "",
   910  					},
   911  				},
   912  			},
   913  		},
   914  		{
   915  			name:                   "CSRSigningControllerOptions specific file set along with cluster single signing file",
   916  			expectErrors:           true,
   917  			expectedErrorSubString: "cannot specify --cluster-signing-{cert,key}-file and other --cluster-signing-*-file flags at the same time",
   918  			options: &CSRSigningControllerOptions{
   919  				&csrsigningconfig.CSRSigningControllerConfiguration{
   920  					ClusterSigningCertFile: "/cluster-signing-cert-file",
   921  					ClusterSigningKeyFile:  "/cluster-signing-key-file",
   922  					ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour},
   923  					KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   924  						CertFile: "/cluster-signing-kubelet-serving/cert-file",
   925  						KeyFile:  "",
   926  					},
   927  					KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   928  						CertFile: "",
   929  						KeyFile:  "",
   930  					},
   931  					KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   932  						CertFile: "",
   933  						KeyFile:  "",
   934  					},
   935  					LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{
   936  						CertFile: "",
   937  						KeyFile:  "",
   938  					},
   939  				},
   940  			},
   941  		},
   942  		{
   943  			name:                   "EndpointSliceControllerOptions ConcurrentServiceEndpointSyncs lower than minConcurrentServiceEndpointSyncs (1)",
   944  			expectErrors:           true,
   945  			expectedErrorSubString: "concurrent-service-endpoint-syncs must not be less than 1",
   946  			options: &EndpointSliceControllerOptions{
   947  				&endpointsliceconfig.EndpointSliceControllerConfiguration{
   948  					ConcurrentServiceEndpointSyncs: 0,
   949  					MaxEndpointsPerSlice:           200,
   950  				},
   951  			},
   952  		},
   953  		{
   954  			name:                   "EndpointSliceControllerOptions ConcurrentServiceEndpointSyncs greater than maxConcurrentServiceEndpointSyncs (50)",
   955  			expectErrors:           true,
   956  			expectedErrorSubString: "concurrent-service-endpoint-syncs must not be more than 50",
   957  			options: &EndpointSliceControllerOptions{
   958  				&endpointsliceconfig.EndpointSliceControllerConfiguration{
   959  					ConcurrentServiceEndpointSyncs: 51,
   960  					MaxEndpointsPerSlice:           200,
   961  				},
   962  			},
   963  		},
   964  		{
   965  			name:                   "EndpointSliceControllerOptions MaxEndpointsPerSlice lower than minMaxEndpointsPerSlice (1)",
   966  			expectErrors:           true,
   967  			expectedErrorSubString: "max-endpoints-per-slice must not be less than 1",
   968  			options: &EndpointSliceControllerOptions{
   969  				&endpointsliceconfig.EndpointSliceControllerConfiguration{
   970  					ConcurrentServiceEndpointSyncs: 10,
   971  					MaxEndpointsPerSlice:           0,
   972  				},
   973  			},
   974  		},
   975  		{
   976  			name:                   "EndpointSliceControllerOptions MaxEndpointsPerSlice greater than maxMaxEndpointsPerSlice (1000)",
   977  			expectErrors:           true,
   978  			expectedErrorSubString: "max-endpoints-per-slice must not be more than 1000",
   979  			options: &EndpointSliceControllerOptions{
   980  				&endpointsliceconfig.EndpointSliceControllerConfiguration{
   981  					ConcurrentServiceEndpointSyncs: 10,
   982  					MaxEndpointsPerSlice:           1001,
   983  				},
   984  			},
   985  		},
   986  		{
   987  			name:                   "EndpointSliceMirroringControllerOptions MirroringConcurrentServiceEndpointSyncs lower than mirroringMinConcurrentServiceEndpointSyncs (1)",
   988  			expectErrors:           true,
   989  			expectedErrorSubString: "mirroring-concurrent-service-endpoint-syncs must not be less than 1",
   990  			options: &EndpointSliceMirroringControllerOptions{
   991  				&endpointslicemirroringconfig.EndpointSliceMirroringControllerConfiguration{
   992  					MirroringConcurrentServiceEndpointSyncs: 0,
   993  					MirroringMaxEndpointsPerSubset:          100,
   994  				},
   995  			},
   996  		},
   997  		{
   998  			name:                   "EndpointSliceMirroringControllerOptions MirroringConcurrentServiceEndpointSyncs greater than mirroringMaxConcurrentServiceEndpointSyncs (50)",
   999  			expectErrors:           true,
  1000  			expectedErrorSubString: "mirroring-concurrent-service-endpoint-syncs must not be more than 50",
  1001  			options: &EndpointSliceMirroringControllerOptions{
  1002  				&endpointslicemirroringconfig.EndpointSliceMirroringControllerConfiguration{
  1003  					MirroringConcurrentServiceEndpointSyncs: 51,
  1004  					MirroringMaxEndpointsPerSubset:          100,
  1005  				},
  1006  			},
  1007  		},
  1008  		{
  1009  			name:                   "EndpointSliceMirroringControllerOptions MirroringMaxEndpointsPerSubset lower than mirroringMinMaxEndpointsPerSubset (1)",
  1010  			expectErrors:           true,
  1011  			expectedErrorSubString: "mirroring-max-endpoints-per-subset must not be less than 1",
  1012  			options: &EndpointSliceMirroringControllerOptions{
  1013  				&endpointslicemirroringconfig.EndpointSliceMirroringControllerConfiguration{
  1014  					MirroringConcurrentServiceEndpointSyncs: 10,
  1015  					MirroringMaxEndpointsPerSubset:          0,
  1016  				},
  1017  			},
  1018  		},
  1019  		{
  1020  			name:                   "EndpointSliceMirroringControllerOptions MirroringMaxEndpointsPerSubset greater than mirroringMaxMaxEndpointsPerSubset (1000)",
  1021  			expectErrors:           true,
  1022  			expectedErrorSubString: "mirroring-max-endpoints-per-subset must not be more than 1000",
  1023  			options: &EndpointSliceMirroringControllerOptions{
  1024  				&endpointslicemirroringconfig.EndpointSliceMirroringControllerConfiguration{
  1025  					MirroringConcurrentServiceEndpointSyncs: 10,
  1026  					MirroringMaxEndpointsPerSubset:          1001,
  1027  				},
  1028  			},
  1029  		},
  1030  		{
  1031  			name:                   "EphemeralVolumeControllerOptions ConcurrentEphemeralVolumeSyncs equal 0",
  1032  			expectErrors:           true,
  1033  			expectedErrorSubString: "concurrent-ephemeralvolume-syncs must be greater than 0",
  1034  			options: &EphemeralVolumeControllerOptions{
  1035  				&ephemeralvolumeconfig.EphemeralVolumeControllerConfiguration{
  1036  					ConcurrentEphemeralVolumeSyncs: 0,
  1037  				},
  1038  			},
  1039  		},
  1040  		{
  1041  			name:                   "HPAControllerOptions ConcurrentHorizontalPodAutoscalerSyncs equal 0",
  1042  			expectErrors:           true,
  1043  			expectedErrorSubString: "concurrent-horizontal-pod-autoscaler-syncs must be greater than 0",
  1044  			options: &HPAControllerOptions{
  1045  				&poautosclerconfig.HPAControllerConfiguration{
  1046  					ConcurrentHorizontalPodAutoscalerSyncs:              0,
  1047  					HorizontalPodAutoscalerSyncPeriod:                   metav1.Duration{Duration: 45 * time.Second},
  1048  					HorizontalPodAutoscalerUpscaleForbiddenWindow:       metav1.Duration{Duration: 1 * time.Minute},
  1049  					HorizontalPodAutoscalerDownscaleForbiddenWindow:     metav1.Duration{Duration: 2 * time.Minute},
  1050  					HorizontalPodAutoscalerDownscaleStabilizationWindow: metav1.Duration{Duration: 3 * time.Minute},
  1051  					HorizontalPodAutoscalerCPUInitializationPeriod:      metav1.Duration{Duration: 90 * time.Second},
  1052  					HorizontalPodAutoscalerInitialReadinessDelay:        metav1.Duration{Duration: 50 * time.Second},
  1053  					HorizontalPodAutoscalerTolerance:                    0.1,
  1054  				},
  1055  			},
  1056  		},
  1057  		{
  1058  			name:                   "NodeIPAMControllerOptions service cluster ip range more than two entries",
  1059  			expectErrors:           true,
  1060  			expectedErrorSubString: "--service-cluster-ip-range can not contain more than two entries",
  1061  			options: &NodeIPAMControllerOptions{
  1062  				&nodeipamconfig.NodeIPAMControllerConfiguration{
  1063  					ServiceCIDR:          "10.0.0.0/16,244.0.0.0/16,3000::/108",
  1064  					NodeCIDRMaskSize:     48,
  1065  					NodeCIDRMaskSizeIPv4: 48,
  1066  					NodeCIDRMaskSizeIPv6: 108,
  1067  				},
  1068  			},
  1069  		},
  1070  		{
  1071  			name:                   "StatefulSetControllerOptions ConcurrentStatefulSetSyncs equal 0",
  1072  			expectErrors:           true,
  1073  			expectedErrorSubString: "concurrent-statefulset-syncs must be greater than 0",
  1074  			options: &StatefulSetControllerOptions{
  1075  				&statefulsetconfig.StatefulSetControllerConfiguration{
  1076  					ConcurrentStatefulSetSyncs: 0,
  1077  				},
  1078  			},
  1079  		},
  1080  		{
  1081  			name:                   "JobControllerOptions ConcurrentJobSyncs equal 0",
  1082  			expectErrors:           true,
  1083  			expectedErrorSubString: "concurrent-job-syncs must be greater than 0",
  1084  			options: &JobControllerOptions{
  1085  				&jobconfig.JobControllerConfiguration{
  1086  					ConcurrentJobSyncs: 0,
  1087  				},
  1088  			},
  1089  		},
  1090  		{
  1091  			name:                   "CronJobControllerOptions ConcurrentCronJobSyncs equal 0",
  1092  			expectErrors:           true,
  1093  			expectedErrorSubString: "concurrent-cron-job-syncs must be greater than 0",
  1094  			options: &CronJobControllerOptions{
  1095  				&cronjobconfig.CronJobControllerConfiguration{
  1096  					ConcurrentCronJobSyncs: 0,
  1097  				},
  1098  			},
  1099  		},
  1100  		/* empty errs */
  1101  		{
  1102  			name:         "CronJobControllerOptions",
  1103  			expectErrors: false,
  1104  			options: &CronJobControllerOptions{
  1105  				&cronjobconfig.CronJobControllerConfiguration{
  1106  					ConcurrentCronJobSyncs: 10,
  1107  				},
  1108  			},
  1109  		},
  1110  		{
  1111  			name:         "DaemonSetControllerOptions",
  1112  			expectErrors: false,
  1113  			options: &DaemonSetControllerOptions{
  1114  				&daemonconfig.DaemonSetControllerConfiguration{
  1115  					ConcurrentDaemonSetSyncs: 2,
  1116  				},
  1117  			},
  1118  		},
  1119  		{
  1120  			name:         "DeploymentControllerOptions",
  1121  			expectErrors: false,
  1122  			options: &DeploymentControllerOptions{
  1123  				&deploymentconfig.DeploymentControllerConfiguration{
  1124  					ConcurrentDeploymentSyncs: 10,
  1125  				},
  1126  			},
  1127  		},
  1128  		{
  1129  			name:         "DeprecatedControllerOptions",
  1130  			expectErrors: false,
  1131  			options: &DeprecatedControllerOptions{
  1132  				&kubectrlmgrconfig.DeprecatedControllerConfiguration{},
  1133  			},
  1134  		},
  1135  		{
  1136  			name:         "EndpointControllerOptions",
  1137  			expectErrors: false,
  1138  			options: &EndpointControllerOptions{
  1139  				&endpointconfig.EndpointControllerConfiguration{
  1140  					ConcurrentEndpointSyncs: 10,
  1141  				},
  1142  			},
  1143  		},
  1144  		{
  1145  			name:         "GarbageCollectorControllerOptions",
  1146  			expectErrors: false,
  1147  			options: &GarbageCollectorControllerOptions{
  1148  				&garbagecollectorconfig.GarbageCollectorControllerConfiguration{
  1149  					ConcurrentGCSyncs: 30,
  1150  					GCIgnoredResources: []garbagecollectorconfig.GroupResource{
  1151  						{Group: "", Resource: "events"},
  1152  						{Group: eventv1.GroupName, Resource: "events"},
  1153  					},
  1154  					EnableGarbageCollector: false,
  1155  				},
  1156  			},
  1157  		},
  1158  		{
  1159  			name:         "JobControllerOptions",
  1160  			expectErrors: false,
  1161  			options: &JobControllerOptions{
  1162  				&jobconfig.JobControllerConfiguration{
  1163  					ConcurrentJobSyncs: 10,
  1164  				},
  1165  			},
  1166  		},
  1167  		{
  1168  			name:         "NamespaceControllerOptions",
  1169  			expectErrors: false,
  1170  			options: &NamespaceControllerOptions{
  1171  				&namespaceconfig.NamespaceControllerConfiguration{
  1172  					NamespaceSyncPeriod:      metav1.Duration{Duration: 10 * time.Minute},
  1173  					ConcurrentNamespaceSyncs: 20,
  1174  				},
  1175  			},
  1176  		},
  1177  		{
  1178  			name:         "NodeLifecycleControllerOptions",
  1179  			expectErrors: false,
  1180  			options: &NodeLifecycleControllerOptions{
  1181  				&nodelifecycleconfig.NodeLifecycleControllerConfiguration{
  1182  					NodeEvictionRate:          0.2,
  1183  					SecondaryNodeEvictionRate: 0.05,
  1184  					NodeMonitorGracePeriod:    metav1.Duration{Duration: 30 * time.Second},
  1185  					NodeStartupGracePeriod:    metav1.Duration{Duration: 30 * time.Second},
  1186  					LargeClusterSizeThreshold: 100,
  1187  					UnhealthyZoneThreshold:    0.6,
  1188  				},
  1189  			},
  1190  		},
  1191  		{
  1192  			name:         "PodGCControllerOptions",
  1193  			expectErrors: false,
  1194  			options: &PodGCControllerOptions{
  1195  				&podgcconfig.PodGCControllerConfiguration{
  1196  					TerminatedPodGCThreshold: 12000,
  1197  				},
  1198  			},
  1199  		},
  1200  		{
  1201  			name:         "ReplicaSetControllerOptions",
  1202  			expectErrors: false,
  1203  			options: &ReplicaSetControllerOptions{
  1204  				&replicasetconfig.ReplicaSetControllerConfiguration{
  1205  					ConcurrentRSSyncs: 10,
  1206  				},
  1207  			},
  1208  		},
  1209  		{
  1210  			name:         "ReplicationControllerOptions",
  1211  			expectErrors: false,
  1212  			options: &ReplicationControllerOptions{
  1213  				&replicationconfig.ReplicationControllerConfiguration{
  1214  					ConcurrentRCSyncs: 10,
  1215  				},
  1216  			},
  1217  		},
  1218  		{
  1219  			name:         "ResourceQuotaControllerOptions",
  1220  			expectErrors: false,
  1221  			options: &ResourceQuotaControllerOptions{
  1222  				&resourcequotaconfig.ResourceQuotaControllerConfiguration{
  1223  					ResourceQuotaSyncPeriod:      metav1.Duration{Duration: 10 * time.Minute},
  1224  					ConcurrentResourceQuotaSyncs: 10,
  1225  				},
  1226  			},
  1227  		},
  1228  		{
  1229  			name:         "SAControllerOptions",
  1230  			expectErrors: false,
  1231  			options: &SAControllerOptions{
  1232  				&serviceaccountconfig.SAControllerConfiguration{
  1233  					ServiceAccountKeyFile:  "/service-account-private-key",
  1234  					ConcurrentSATokenSyncs: 10,
  1235  				},
  1236  			},
  1237  		},
  1238  		{
  1239  			name:         "LegacySATokenCleanerOptions",
  1240  			expectErrors: false,
  1241  			options: &LegacySATokenCleanerOptions{
  1242  				&serviceaccountconfig.LegacySATokenCleanerConfiguration{
  1243  					CleanUpPeriod: metav1.Duration{Duration: 24 * 365 * time.Hour},
  1244  				},
  1245  			},
  1246  		},
  1247  		{
  1248  			name:         "TTLAfterFinishedControllerOptions",
  1249  			expectErrors: false,
  1250  			options: &TTLAfterFinishedControllerOptions{
  1251  				&ttlafterfinishedconfig.TTLAfterFinishedControllerConfiguration{
  1252  					ConcurrentTTLSyncs: 8,
  1253  				},
  1254  			},
  1255  		},
  1256  	}
  1257  
  1258  	for _, tc := range testCases {
  1259  		t.Run(tc.name, func(t *testing.T) {
  1260  			errs := tc.options.Validate()
  1261  			if len(errs) > 0 && !tc.expectErrors {
  1262  				t.Errorf("expected no errors, errors found %+v", errs)
  1263  			}
  1264  
  1265  			if len(errs) == 0 && tc.expectErrors {
  1266  				t.Errorf("expected errors, no errors found")
  1267  			}
  1268  
  1269  			if len(errs) > 0 && tc.expectErrors {
  1270  				gotErr := utilerrors.NewAggregate(errs).Error()
  1271  				if !strings.Contains(gotErr, tc.expectedErrorSubString) {
  1272  					t.Errorf("expected error: %s, got err: %v", tc.expectedErrorSubString, gotErr)
  1273  				}
  1274  			}
  1275  		})
  1276  	}
  1277  }
  1278  
  1279  func TestValidateControllerManagerOptions(t *testing.T) {
  1280  	opts, err := NewKubeControllerManagerOptions()
  1281  	if err != nil {
  1282  		t.Errorf("expected no error, error found %+v", err)
  1283  	}
  1284  
  1285  	opts.EndpointSliceController.MaxEndpointsPerSlice = 1001 // max endpoints per slice should be a positive integer <= 1000
  1286  
  1287  	if err := opts.Validate([]string{"*"}, []string{""}, nil); err == nil {
  1288  		t.Error("expected error, no error found")
  1289  	}
  1290  }
  1291  
  1292  func TestControllerManagerAliases(t *testing.T) {
  1293  	opts, err := NewKubeControllerManagerOptions()
  1294  	if err != nil {
  1295  		t.Errorf("expected no error, error found %+v", err)
  1296  	}
  1297  	opts.Generic.Controllers = []string{"deployment", "-job", "-cronjob-controller", "podgc", "token-cleaner-controller"}
  1298  	expectedControllers := []string{"deployment-controller", "-job-controller", "-cronjob-controller", "pod-garbage-collector-controller", "token-cleaner-controller"}
  1299  
  1300  	allControllers := []string{
  1301  		"bootstrap-signer-controller",
  1302  		"job-controller",
  1303  		"deployment-controller",
  1304  		"cronjob-controller",
  1305  		"namespace-controller",
  1306  		"pod-garbage-collector-controller",
  1307  		"token-cleaner-controller",
  1308  	}
  1309  	disabledByDefaultControllers := []string{
  1310  		"bootstrap-signer-controller",
  1311  		"token-cleaner-controller",
  1312  	}
  1313  	controllerAliases := map[string]string{
  1314  		"bootstrapsigner": "bootstrap-signer-controller",
  1315  		"job":             "job-controller",
  1316  		"deployment":      "deployment-controller",
  1317  		"cronjob":         "cronjob-controller",
  1318  		"namespace":       "namespace-controller",
  1319  		"podgc":           "pod-garbage-collector-controller",
  1320  		"tokencleaner":    "token-cleaner-controller",
  1321  	}
  1322  
  1323  	if err := opts.Validate(allControllers, disabledByDefaultControllers, controllerAliases); err != nil {
  1324  		t.Errorf("expected no error, error found %v", err)
  1325  	}
  1326  
  1327  	cfg := &kubecontrollerconfig.Config{}
  1328  	if err := opts.ApplyTo(cfg, allControllers, disabledByDefaultControllers, controllerAliases); err != nil {
  1329  		t.Errorf("expected no error, error found %v", err)
  1330  	}
  1331  	if !reflect.DeepEqual(cfg.ComponentConfig.Generic.Controllers, expectedControllers) {
  1332  		t.Errorf("controller aliases not resolved correctly, expected %+v, got %+v", expectedControllers, cfg.ComponentConfig.Generic.Controllers)
  1333  	}
  1334  }
  1335  
  1336  func TestWatchListClientFlagUsage(t *testing.T) {
  1337  	assertWatchListClientFeatureDefaultValue(t)
  1338  
  1339  	fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
  1340  	s, _ := NewKubeControllerManagerOptions()
  1341  	for _, f := range s.Flags([]string{""}, []string{""}, nil).FlagSets {
  1342  		fs.AddFlagSet(f)
  1343  	}
  1344  
  1345  	fgFlagName := "feature-gates"
  1346  	fg := fs.Lookup(fgFlagName)
  1347  	if fg == nil {
  1348  		t.Fatalf("didn't find %q flag", fgFlagName)
  1349  	}
  1350  
  1351  	expectedWatchListClientString := "WatchListClient=true|false (BETA - default=false)"
  1352  	if !strings.Contains(fg.Usage, expectedWatchListClientString) {
  1353  		t.Fatalf("%q flag doesn't contain the expected usage for %v feature gate.\nExpected = %v\nUsage = %v", fgFlagName, clientgofeaturegate.WatchListClient, expectedWatchListClientString, fg.Usage)
  1354  	}
  1355  }
  1356  
  1357  func TestWatchListClientFlagChange(t *testing.T) {
  1358  	assertWatchListClientFeatureDefaultValue(t)
  1359  
  1360  	fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
  1361  	s, _ := NewKubeControllerManagerOptions()
  1362  	for _, f := range s.Flags([]string{""}, []string{""}, nil).FlagSets {
  1363  		fs.AddFlagSet(f)
  1364  	}
  1365  
  1366  	args := []string{fmt.Sprintf("--feature-gates=%v=true", clientgofeaturegate.WatchListClient)}
  1367  	if err := fs.Parse(args); err != nil {
  1368  		t.Fatal(err)
  1369  	}
  1370  
  1371  	watchListClientValue := clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.WatchListClient)
  1372  	if !watchListClientValue {
  1373  		t.Fatalf("expected %q feature gate to be enabled after setting the command line flag", clientgofeaturegate.WatchListClient)
  1374  	}
  1375  }
  1376  
  1377  func assertWatchListClientFeatureDefaultValue(t *testing.T) {
  1378  	watchListClientDefaultValue := clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.WatchListClient)
  1379  	if watchListClientDefaultValue {
  1380  		t.Fatalf("expected %q feature gate to be disabled for KCM", clientgofeaturegate.WatchListClient)
  1381  	}
  1382  }
  1383  
  1384  type sortedGCIgnoredResources []garbagecollectorconfig.GroupResource
  1385  
  1386  func (r sortedGCIgnoredResources) Len() int {
  1387  	return len(r)
  1388  }
  1389  
  1390  func (r sortedGCIgnoredResources) Less(i, j int) bool {
  1391  	if r[i].Group < r[j].Group {
  1392  		return true
  1393  	} else if r[i].Group > r[j].Group {
  1394  		return false
  1395  	}
  1396  	return r[i].Resource < r[j].Resource
  1397  }
  1398  
  1399  func (r sortedGCIgnoredResources) Swap(i, j int) {
  1400  	r[i], r[j] = r[j], r[i]
  1401  }
  1402  

View as plain text