...

Source file src/k8s.io/kubernetes/pkg/scheduler/apis/config/scheme/scheme_test.go

Documentation: k8s.io/kubernetes/pkg/scheduler/apis/config/scheme

     1  /*
     2  Copyright 2020 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 scheme
    18  
    19  import (
    20  	"bytes"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	corev1 "k8s.io/api/core/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  	v1 "k8s.io/kube-scheduler/config/v1"
    28  	"k8s.io/kubernetes/pkg/scheduler/apis/config"
    29  	"k8s.io/kubernetes/pkg/scheduler/apis/config/testing/defaults"
    30  	"k8s.io/utils/ptr"
    31  	"sigs.k8s.io/yaml"
    32  )
    33  
    34  // TestCodecsDecodePluginConfig tests that embedded plugin args get decoded
    35  // into their appropriate internal types and defaults are applied.
    36  func TestCodecsDecodePluginConfig(t *testing.T) {
    37  	testCases := []struct {
    38  		name         string
    39  		data         []byte
    40  		wantErr      string
    41  		wantProfiles []config.KubeSchedulerProfile
    42  	}{
    43  		// v1 tests
    44  		{
    45  			name: "v1 all plugin args in default profile",
    46  			data: []byte(`
    47  apiVersion: kubescheduler.config.k8s.io/v1
    48  kind: KubeSchedulerConfiguration
    49  profiles:
    50  - pluginConfig:
    51    - name: DefaultPreemption
    52      args:
    53        minCandidateNodesPercentage: 50
    54        minCandidateNodesAbsolute: 500
    55    - name: InterPodAffinity
    56      args:
    57        hardPodAffinityWeight: 5
    58    - name: NodeResourcesFit
    59      args:
    60        ignoredResources: ["foo"]
    61    - name: PodTopologySpread
    62      args:
    63        defaultConstraints:
    64        - maxSkew: 1
    65          topologyKey: zone
    66          whenUnsatisfiable: ScheduleAnyway
    67    - name: VolumeBinding
    68      args:
    69        bindTimeoutSeconds: 300
    70    - name: NodeAffinity
    71      args:
    72        addedAffinity:
    73          requiredDuringSchedulingIgnoredDuringExecution:
    74            nodeSelectorTerms:
    75            - matchExpressions:
    76              - key: foo
    77                operator: In
    78                values: ["bar"]
    79    - name: NodeResourcesBalancedAllocation
    80      args:
    81        resources:
    82          - name: cpu       # default weight(1) will be set.
    83          - name: memory    # weight 0 will be replaced by 1.
    84            weight: 0
    85          - name: scalar0
    86            weight: 1
    87          - name: scalar1   # default weight(1) will be set for scalar1
    88          - name: scalar2   # weight 0 will be replaced by 1.
    89            weight: 0
    90          - name: scalar3
    91            weight: 2
    92  `),
    93  			wantProfiles: []config.KubeSchedulerProfile{
    94  				{
    95  					SchedulerName:            "default-scheduler",
    96  					PercentageOfNodesToScore: nil,
    97  					Plugins:                  defaults.PluginsV1,
    98  					PluginConfig: []config.PluginConfig{
    99  						{
   100  							Name: "DefaultPreemption",
   101  							Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 50, MinCandidateNodesAbsolute: 500},
   102  						},
   103  						{
   104  							Name: "InterPodAffinity",
   105  							Args: &config.InterPodAffinityArgs{HardPodAffinityWeight: 5},
   106  						},
   107  						{
   108  							Name: "NodeResourcesFit",
   109  							Args: &config.NodeResourcesFitArgs{
   110  								IgnoredResources: []string{"foo"},
   111  								ScoringStrategy: &config.ScoringStrategy{
   112  									Type: config.LeastAllocated,
   113  									Resources: []config.ResourceSpec{
   114  										{Name: "cpu", Weight: 1},
   115  										{Name: "memory", Weight: 1},
   116  									},
   117  								},
   118  							},
   119  						},
   120  						{
   121  							Name: "PodTopologySpread",
   122  							Args: &config.PodTopologySpreadArgs{
   123  								DefaultConstraints: []corev1.TopologySpreadConstraint{
   124  									{MaxSkew: 1, TopologyKey: "zone", WhenUnsatisfiable: corev1.ScheduleAnyway},
   125  								},
   126  								DefaultingType: config.SystemDefaulting,
   127  							},
   128  						},
   129  						{
   130  							Name: "VolumeBinding",
   131  							Args: &config.VolumeBindingArgs{
   132  								BindTimeoutSeconds: 300,
   133  							},
   134  						},
   135  						{
   136  							Name: "NodeAffinity",
   137  							Args: &config.NodeAffinityArgs{
   138  								AddedAffinity: &corev1.NodeAffinity{
   139  									RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
   140  										NodeSelectorTerms: []corev1.NodeSelectorTerm{
   141  											{
   142  												MatchExpressions: []corev1.NodeSelectorRequirement{
   143  													{
   144  														Key:      "foo",
   145  														Operator: corev1.NodeSelectorOpIn,
   146  														Values:   []string{"bar"},
   147  													},
   148  												},
   149  											},
   150  										},
   151  									},
   152  								},
   153  							},
   154  						},
   155  						{
   156  							Name: "NodeResourcesBalancedAllocation",
   157  							Args: &config.NodeResourcesBalancedAllocationArgs{
   158  								Resources: []config.ResourceSpec{
   159  									{Name: "cpu", Weight: 1},
   160  									{Name: "memory", Weight: 1},
   161  									{Name: "scalar0", Weight: 1},
   162  									{Name: "scalar1", Weight: 1},
   163  									{Name: "scalar2", Weight: 1},
   164  									{Name: "scalar3", Weight: 2}},
   165  							},
   166  						},
   167  					},
   168  				},
   169  			},
   170  		},
   171  		{
   172  			name: "v1 with non-default global percentageOfNodesToScore",
   173  			data: []byte(`
   174  apiVersion: kubescheduler.config.k8s.io/v1
   175  kind: KubeSchedulerConfiguration
   176  percentageOfNodesToScore: 10
   177  `),
   178  			wantProfiles: []config.KubeSchedulerProfile{
   179  				{
   180  					SchedulerName:            "default-scheduler",
   181  					PercentageOfNodesToScore: nil,
   182  					Plugins:                  defaults.PluginsV1,
   183  					PluginConfig:             defaults.PluginConfigsV1,
   184  				},
   185  			},
   186  		},
   187  		{
   188  			name: "v1 with non-default global and profile percentageOfNodesToScore",
   189  			data: []byte(`
   190  apiVersion: kubescheduler.config.k8s.io/v1
   191  kind: KubeSchedulerConfiguration
   192  percentageOfNodesToScore: 10
   193  profiles:
   194  - percentageOfNodesToScore: 20
   195  `),
   196  			wantProfiles: []config.KubeSchedulerProfile{
   197  				{
   198  					SchedulerName:            "default-scheduler",
   199  					PercentageOfNodesToScore: ptr.To[int32](20),
   200  					Plugins:                  defaults.PluginsV1,
   201  					PluginConfig:             defaults.PluginConfigsV1,
   202  				},
   203  			},
   204  		},
   205  		{
   206  			name: "v1 plugins can include version and kind",
   207  			data: []byte(`
   208  apiVersion: kubescheduler.config.k8s.io/v1
   209  kind: KubeSchedulerConfiguration
   210  profiles:
   211  - pluginConfig:
   212    - name: DefaultPreemption
   213      args:
   214        apiVersion: kubescheduler.config.k8s.io/v1
   215        kind: DefaultPreemptionArgs
   216        minCandidateNodesPercentage: 50
   217  `),
   218  			wantProfiles: []config.KubeSchedulerProfile{
   219  				{
   220  					SchedulerName: "default-scheduler",
   221  					Plugins:       defaults.PluginsV1,
   222  					PluginConfig: []config.PluginConfig{
   223  						{
   224  							Name: "DefaultPreemption",
   225  							Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 50, MinCandidateNodesAbsolute: 100},
   226  						},
   227  						{
   228  							Name: "InterPodAffinity",
   229  							Args: &config.InterPodAffinityArgs{
   230  								HardPodAffinityWeight: 1,
   231  							},
   232  						},
   233  						{
   234  							Name: "NodeAffinity",
   235  							Args: &config.NodeAffinityArgs{},
   236  						},
   237  						{
   238  							Name: "NodeResourcesBalancedAllocation",
   239  							Args: &config.NodeResourcesBalancedAllocationArgs{
   240  								Resources: []config.ResourceSpec{{Name: "cpu", Weight: 1}, {Name: "memory", Weight: 1}},
   241  							},
   242  						},
   243  						{
   244  							Name: "NodeResourcesFit",
   245  							Args: &config.NodeResourcesFitArgs{
   246  								ScoringStrategy: &config.ScoringStrategy{
   247  									Type: config.LeastAllocated,
   248  									Resources: []config.ResourceSpec{
   249  										{Name: "cpu", Weight: 1},
   250  										{Name: "memory", Weight: 1},
   251  									},
   252  								},
   253  							},
   254  						},
   255  						{
   256  							Name: "PodTopologySpread",
   257  							Args: &config.PodTopologySpreadArgs{
   258  								DefaultingType: config.SystemDefaulting,
   259  							},
   260  						},
   261  						{
   262  							Name: "VolumeBinding",
   263  							Args: &config.VolumeBindingArgs{
   264  								BindTimeoutSeconds: 600,
   265  							},
   266  						},
   267  					},
   268  				},
   269  			},
   270  		},
   271  		{
   272  			name: "plugin group and kind should match the type",
   273  			data: []byte(`
   274  apiVersion: kubescheduler.config.k8s.io/v1
   275  kind: KubeSchedulerConfiguration
   276  profiles:
   277  - pluginConfig:
   278    - name: DefaultPreemption
   279      args:
   280        apiVersion: kubescheduler.config.k8s.io/v1
   281        kind: InterPodAffinityArgs
   282  `),
   283  			wantErr: `decoding .profiles[0].pluginConfig[0]: args for plugin DefaultPreemption were not of type DefaultPreemptionArgs.kubescheduler.config.k8s.io, got InterPodAffinityArgs.kubescheduler.config.k8s.io`,
   284  		},
   285  		{
   286  			name: "v1 NodResourcesFitArgs shape encoding is strict",
   287  			data: []byte(`
   288  apiVersion: kubescheduler.config.k8s.io/v1
   289  kind: KubeSchedulerConfiguration
   290  profiles:
   291  - pluginConfig:
   292    - name: NodeResourcesFit
   293      args:
   294        scoringStrategy:
   295          requestedToCapacityRatio:
   296            shape:
   297            - Score: 2
   298              Utilization: 1
   299  `),
   300  			wantErr: `strict decoding error: decoding .profiles[0].pluginConfig[0]: strict decoding error: decoding args for plugin NodeResourcesFit: strict decoding error: unknown field "scoringStrategy.requestedToCapacityRatio.shape[0].Score", unknown field "scoringStrategy.requestedToCapacityRatio.shape[0].Utilization"`,
   301  		},
   302  		{
   303  			name: "v1 NodeResourcesFitArgs resources encoding is strict",
   304  			data: []byte(`
   305  apiVersion: kubescheduler.config.k8s.io/v1
   306  kind: KubeSchedulerConfiguration
   307  profiles:
   308  - pluginConfig:
   309    - name: NodeResourcesFit
   310      args:
   311        scoringStrategy:
   312          resources:
   313          - Name: cpu
   314            Weight: 1
   315  `),
   316  			wantErr: `strict decoding error: decoding .profiles[0].pluginConfig[0]: strict decoding error: decoding args for plugin NodeResourcesFit: strict decoding error: unknown field "scoringStrategy.resources[0].Name", unknown field "scoringStrategy.resources[0].Weight"`,
   317  		},
   318  		{
   319  			name: "out-of-tree plugin args",
   320  			data: []byte(`
   321  apiVersion: kubescheduler.config.k8s.io/v1
   322  kind: KubeSchedulerConfiguration
   323  profiles:
   324  - pluginConfig:
   325    - name: OutOfTreePlugin
   326      args:
   327        foo: bar
   328  `),
   329  			wantProfiles: []config.KubeSchedulerProfile{
   330  				{
   331  					SchedulerName: "default-scheduler",
   332  					Plugins:       defaults.PluginsV1,
   333  					PluginConfig: append([]config.PluginConfig{
   334  						{
   335  							Name: "OutOfTreePlugin",
   336  							Args: &runtime.Unknown{
   337  								ContentType: "application/json",
   338  								Raw:         []byte(`{"foo":"bar"}`),
   339  							},
   340  						},
   341  					}, defaults.PluginConfigsV1...),
   342  				},
   343  			},
   344  		},
   345  		{
   346  			name: "empty and no plugin args",
   347  			data: []byte(`
   348  apiVersion: kubescheduler.config.k8s.io/v1
   349  kind: KubeSchedulerConfiguration
   350  profiles:
   351  - pluginConfig:
   352    - name: DefaultPreemption
   353      args:
   354    - name: InterPodAffinity
   355      args:
   356    - name: NodeResourcesFit
   357    - name: OutOfTreePlugin
   358      args:
   359    - name: VolumeBinding
   360      args:
   361    - name: PodTopologySpread
   362    - name: NodeAffinity
   363    - name: NodeResourcesBalancedAllocation
   364  `),
   365  			wantProfiles: []config.KubeSchedulerProfile{
   366  				{
   367  					SchedulerName: "default-scheduler",
   368  					Plugins:       defaults.PluginsV1,
   369  					PluginConfig: []config.PluginConfig{
   370  						{
   371  							Name: "DefaultPreemption",
   372  							Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
   373  						},
   374  						{
   375  							Name: "InterPodAffinity",
   376  							Args: &config.InterPodAffinityArgs{
   377  								HardPodAffinityWeight: 1,
   378  							},
   379  						},
   380  						{
   381  							Name: "NodeResourcesFit",
   382  							Args: &config.NodeResourcesFitArgs{
   383  								ScoringStrategy: &config.ScoringStrategy{
   384  									Type: config.LeastAllocated,
   385  									Resources: []config.ResourceSpec{
   386  										{Name: "cpu", Weight: 1},
   387  										{Name: "memory", Weight: 1},
   388  									},
   389  								},
   390  							},
   391  						},
   392  						{Name: "OutOfTreePlugin"},
   393  						{
   394  							Name: "VolumeBinding",
   395  							Args: &config.VolumeBindingArgs{
   396  								BindTimeoutSeconds: 600,
   397  							},
   398  						},
   399  						{
   400  							Name: "PodTopologySpread",
   401  							Args: &config.PodTopologySpreadArgs{
   402  								DefaultingType: config.SystemDefaulting,
   403  							},
   404  						},
   405  						{
   406  							Name: "NodeAffinity",
   407  							Args: &config.NodeAffinityArgs{},
   408  						},
   409  						{
   410  							Name: "NodeResourcesBalancedAllocation",
   411  							Args: &config.NodeResourcesBalancedAllocationArgs{
   412  								Resources: []config.ResourceSpec{{Name: "cpu", Weight: 1}, {Name: "memory", Weight: 1}},
   413  							},
   414  						},
   415  					},
   416  				},
   417  			},
   418  		},
   419  		{
   420  			name: "ignorePreferredTermsOfExistingPods is enabled",
   421  			data: []byte(`
   422  apiVersion: kubescheduler.config.k8s.io/v1
   423  kind: KubeSchedulerConfiguration
   424  profiles:
   425  - pluginConfig:
   426    - name: InterPodAffinity
   427      args:
   428        ignorePreferredTermsOfExistingPods: true
   429  `),
   430  			wantProfiles: []config.KubeSchedulerProfile{
   431  				{
   432  					SchedulerName: "default-scheduler",
   433  					Plugins:       defaults.PluginsV1,
   434  					PluginConfig: []config.PluginConfig{
   435  						{
   436  							Name: "InterPodAffinity",
   437  							Args: &config.InterPodAffinityArgs{
   438  								HardPodAffinityWeight:              1,
   439  								IgnorePreferredTermsOfExistingPods: true,
   440  							},
   441  						},
   442  						{
   443  							Name: "DefaultPreemption",
   444  							Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
   445  						},
   446  						{
   447  							Name: "NodeAffinity",
   448  							Args: &config.NodeAffinityArgs{},
   449  						},
   450  						{
   451  							Name: "NodeResourcesBalancedAllocation",
   452  							Args: &config.NodeResourcesBalancedAllocationArgs{
   453  								Resources: []config.ResourceSpec{{Name: "cpu", Weight: 1}, {Name: "memory", Weight: 1}},
   454  							},
   455  						},
   456  						{
   457  							Name: "NodeResourcesFit",
   458  							Args: &config.NodeResourcesFitArgs{
   459  								ScoringStrategy: &config.ScoringStrategy{
   460  									Type: config.LeastAllocated,
   461  									Resources: []config.ResourceSpec{
   462  										{Name: "cpu", Weight: 1},
   463  										{Name: "memory", Weight: 1},
   464  									},
   465  								},
   466  							},
   467  						},
   468  						{
   469  							Name: "PodTopologySpread",
   470  							Args: &config.PodTopologySpreadArgs{
   471  								DefaultingType: config.SystemDefaulting,
   472  							},
   473  						},
   474  						{
   475  							Name: "VolumeBinding",
   476  							Args: &config.VolumeBindingArgs{
   477  								BindTimeoutSeconds: 600,
   478  							},
   479  						},
   480  					},
   481  				},
   482  			},
   483  		},
   484  	}
   485  	decoder := Codecs.UniversalDecoder()
   486  	for _, tt := range testCases {
   487  		t.Run(tt.name, func(t *testing.T) {
   488  			obj, gvk, err := decoder.Decode(tt.data, nil, nil)
   489  			if err != nil {
   490  				if tt.wantErr != err.Error() {
   491  					t.Fatalf("\ngot err:\n\t%v\nwant:\n\t%s", err, tt.wantErr)
   492  				}
   493  				return
   494  			}
   495  			if len(tt.wantErr) != 0 {
   496  				t.Fatalf("no error produced, wanted %v", tt.wantErr)
   497  			}
   498  			got, ok := obj.(*config.KubeSchedulerConfiguration)
   499  			if !ok {
   500  				t.Fatalf("decoded into %s, want %s", gvk, config.SchemeGroupVersion.WithKind("KubeSchedulerConfiguration"))
   501  			}
   502  			if diff := cmp.Diff(tt.wantProfiles, got.Profiles); diff != "" {
   503  				t.Errorf("unexpected configuration (-want,+got):\n%s", diff)
   504  			}
   505  		})
   506  	}
   507  }
   508  
   509  func TestCodecsEncodePluginConfig(t *testing.T) {
   510  	testCases := []struct {
   511  		name    string
   512  		obj     runtime.Object
   513  		version schema.GroupVersion
   514  		want    string
   515  	}{
   516  		//v1 tests
   517  		{
   518  			name:    "v1 in-tree and out-of-tree plugins",
   519  			version: v1.SchemeGroupVersion,
   520  			obj: &v1.KubeSchedulerConfiguration{
   521  				Profiles: []v1.KubeSchedulerProfile{
   522  					{
   523  						PluginConfig: []v1.PluginConfig{
   524  							{
   525  								Name: "InterPodAffinity",
   526  								Args: runtime.RawExtension{
   527  									Object: &v1.InterPodAffinityArgs{
   528  										HardPodAffinityWeight: ptr.To[int32](5),
   529  									},
   530  								},
   531  							},
   532  							{
   533  								Name: "VolumeBinding",
   534  								Args: runtime.RawExtension{
   535  									Object: &v1.VolumeBindingArgs{
   536  										BindTimeoutSeconds: ptr.To[int64](300),
   537  										Shape: []v1.UtilizationShapePoint{
   538  											{
   539  												Utilization: 0,
   540  												Score:       0,
   541  											},
   542  											{
   543  												Utilization: 100,
   544  												Score:       10,
   545  											},
   546  										},
   547  									},
   548  								},
   549  							},
   550  							{
   551  								Name: "NodeResourcesFit",
   552  								Args: runtime.RawExtension{
   553  									Object: &v1.NodeResourcesFitArgs{
   554  										ScoringStrategy: &v1.ScoringStrategy{
   555  											Type:      v1.RequestedToCapacityRatio,
   556  											Resources: []v1.ResourceSpec{{Name: "cpu", Weight: 1}},
   557  											RequestedToCapacityRatio: &v1.RequestedToCapacityRatioParam{
   558  												Shape: []v1.UtilizationShapePoint{
   559  													{Utilization: 1, Score: 2},
   560  												},
   561  											},
   562  										},
   563  									},
   564  								},
   565  							},
   566  							{
   567  								Name: "PodTopologySpread",
   568  								Args: runtime.RawExtension{
   569  									Object: &v1.PodTopologySpreadArgs{
   570  										DefaultConstraints: []corev1.TopologySpreadConstraint{},
   571  									},
   572  								},
   573  							},
   574  							{
   575  								Name: "OutOfTreePlugin",
   576  								Args: runtime.RawExtension{
   577  									Raw: []byte(`{"foo":"bar"}`),
   578  								},
   579  							},
   580  						},
   581  					},
   582  				},
   583  			},
   584  			want: `apiVersion: kubescheduler.config.k8s.io/v1
   585  clientConnection:
   586    acceptContentTypes: ""
   587    burst: 0
   588    contentType: ""
   589    kubeconfig: ""
   590    qps: 0
   591  kind: KubeSchedulerConfiguration
   592  leaderElection:
   593    leaderElect: null
   594    leaseDuration: 0s
   595    renewDeadline: 0s
   596    resourceLock: ""
   597    resourceName: ""
   598    resourceNamespace: ""
   599    retryPeriod: 0s
   600  profiles:
   601  - pluginConfig:
   602    - args:
   603        apiVersion: kubescheduler.config.k8s.io/v1
   604        hardPodAffinityWeight: 5
   605        ignorePreferredTermsOfExistingPods: false
   606        kind: InterPodAffinityArgs
   607      name: InterPodAffinity
   608    - args:
   609        apiVersion: kubescheduler.config.k8s.io/v1
   610        bindTimeoutSeconds: 300
   611        kind: VolumeBindingArgs
   612        shape:
   613        - score: 0
   614          utilization: 0
   615        - score: 10
   616          utilization: 100
   617      name: VolumeBinding
   618    - args:
   619        apiVersion: kubescheduler.config.k8s.io/v1
   620        kind: NodeResourcesFitArgs
   621        scoringStrategy:
   622          requestedToCapacityRatio:
   623            shape:
   624            - score: 2
   625              utilization: 1
   626          resources:
   627          - name: cpu
   628            weight: 1
   629          type: RequestedToCapacityRatio
   630      name: NodeResourcesFit
   631    - args:
   632        apiVersion: kubescheduler.config.k8s.io/v1
   633        kind: PodTopologySpreadArgs
   634      name: PodTopologySpread
   635    - args:
   636        foo: bar
   637      name: OutOfTreePlugin
   638  `,
   639  		},
   640  		{
   641  			name:    "v1 in-tree and out-of-tree plugins from internal",
   642  			version: v1.SchemeGroupVersion,
   643  			obj: &config.KubeSchedulerConfiguration{
   644  				Parallelism:           8,
   645  				DelayCacheUntilActive: true,
   646  				Profiles: []config.KubeSchedulerProfile{
   647  					{
   648  						PluginConfig: []config.PluginConfig{
   649  							{
   650  								Name: "InterPodAffinity",
   651  								Args: &config.InterPodAffinityArgs{
   652  									HardPodAffinityWeight: 5,
   653  								},
   654  							},
   655  							{
   656  								Name: "NodeResourcesFit",
   657  								Args: &config.NodeResourcesFitArgs{
   658  									ScoringStrategy: &config.ScoringStrategy{
   659  										Type:      config.LeastAllocated,
   660  										Resources: []config.ResourceSpec{{Name: "cpu", Weight: 1}},
   661  									},
   662  								},
   663  							},
   664  							{
   665  								Name: "VolumeBinding",
   666  								Args: &config.VolumeBindingArgs{
   667  									BindTimeoutSeconds: 300,
   668  								},
   669  							},
   670  							{
   671  								Name: "PodTopologySpread",
   672  								Args: &config.PodTopologySpreadArgs{},
   673  							},
   674  							{
   675  								Name: "OutOfTreePlugin",
   676  								Args: &runtime.Unknown{
   677  									Raw: []byte(`{"foo":"bar"}`),
   678  								},
   679  							},
   680  						},
   681  					},
   682  				},
   683  			},
   684  			want: `apiVersion: kubescheduler.config.k8s.io/v1
   685  clientConnection:
   686    acceptContentTypes: ""
   687    burst: 0
   688    contentType: ""
   689    kubeconfig: ""
   690    qps: 0
   691  delayCacheUntilActive: true
   692  enableContentionProfiling: false
   693  enableProfiling: false
   694  kind: KubeSchedulerConfiguration
   695  leaderElection:
   696    leaderElect: false
   697    leaseDuration: 0s
   698    renewDeadline: 0s
   699    resourceLock: ""
   700    resourceName: ""
   701    resourceNamespace: ""
   702    retryPeriod: 0s
   703  parallelism: 8
   704  podInitialBackoffSeconds: 0
   705  podMaxBackoffSeconds: 0
   706  profiles:
   707  - pluginConfig:
   708    - args:
   709        apiVersion: kubescheduler.config.k8s.io/v1
   710        hardPodAffinityWeight: 5
   711        ignorePreferredTermsOfExistingPods: false
   712        kind: InterPodAffinityArgs
   713      name: InterPodAffinity
   714    - args:
   715        apiVersion: kubescheduler.config.k8s.io/v1
   716        kind: NodeResourcesFitArgs
   717        scoringStrategy:
   718          resources:
   719          - name: cpu
   720            weight: 1
   721          type: LeastAllocated
   722      name: NodeResourcesFit
   723    - args:
   724        apiVersion: kubescheduler.config.k8s.io/v1
   725        bindTimeoutSeconds: 300
   726        kind: VolumeBindingArgs
   727      name: VolumeBinding
   728    - args:
   729        apiVersion: kubescheduler.config.k8s.io/v1
   730        kind: PodTopologySpreadArgs
   731      name: PodTopologySpread
   732    - args:
   733        foo: bar
   734      name: OutOfTreePlugin
   735    schedulerName: ""
   736  `,
   737  		},
   738  		{
   739  			name:    "v1 ignorePreferredTermsOfExistingPods is enabled",
   740  			version: v1.SchemeGroupVersion,
   741  			obj: &config.KubeSchedulerConfiguration{
   742  				Parallelism:           8,
   743  				DelayCacheUntilActive: true,
   744  				Profiles: []config.KubeSchedulerProfile{
   745  					{
   746  						PluginConfig: []config.PluginConfig{
   747  							{
   748  								Name: "InterPodAffinity",
   749  								Args: &config.InterPodAffinityArgs{
   750  									HardPodAffinityWeight:              5,
   751  									IgnorePreferredTermsOfExistingPods: true,
   752  								},
   753  							},
   754  						},
   755  					},
   756  				},
   757  			},
   758  			want: `apiVersion: kubescheduler.config.k8s.io/v1
   759  clientConnection:
   760    acceptContentTypes: ""
   761    burst: 0
   762    contentType: ""
   763    kubeconfig: ""
   764    qps: 0
   765  delayCacheUntilActive: true
   766  enableContentionProfiling: false
   767  enableProfiling: false
   768  kind: KubeSchedulerConfiguration
   769  leaderElection:
   770    leaderElect: false
   771    leaseDuration: 0s
   772    renewDeadline: 0s
   773    resourceLock: ""
   774    resourceName: ""
   775    resourceNamespace: ""
   776    retryPeriod: 0s
   777  parallelism: 8
   778  podInitialBackoffSeconds: 0
   779  podMaxBackoffSeconds: 0
   780  profiles:
   781  - pluginConfig:
   782    - args:
   783        apiVersion: kubescheduler.config.k8s.io/v1
   784        hardPodAffinityWeight: 5
   785        ignorePreferredTermsOfExistingPods: true
   786        kind: InterPodAffinityArgs
   787      name: InterPodAffinity
   788    schedulerName: ""
   789  `,
   790  		},
   791  	}
   792  	yamlInfo, ok := runtime.SerializerInfoForMediaType(Codecs.SupportedMediaTypes(), runtime.ContentTypeYAML)
   793  	if !ok {
   794  		t.Fatalf("unable to locate encoder -- %q is not a supported media type", runtime.ContentTypeYAML)
   795  	}
   796  	jsonInfo, ok := runtime.SerializerInfoForMediaType(Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
   797  	if !ok {
   798  		t.Fatalf("unable to locate encoder -- %q is not a supported media type", runtime.ContentTypeJSON)
   799  	}
   800  	for _, tt := range testCases {
   801  		t.Run(tt.name, func(t *testing.T) {
   802  			encoder := Codecs.EncoderForVersion(yamlInfo.Serializer, tt.version)
   803  			var buf bytes.Buffer
   804  			if err := encoder.Encode(tt.obj, &buf); err != nil {
   805  				t.Fatal(err)
   806  			}
   807  			if diff := cmp.Diff(tt.want, buf.String()); diff != "" {
   808  				t.Errorf("unexpected encoded configuration:\n%s", diff)
   809  			}
   810  			encoder = Codecs.EncoderForVersion(jsonInfo.Serializer, tt.version)
   811  			buf = bytes.Buffer{}
   812  			if err := encoder.Encode(tt.obj, &buf); err != nil {
   813  				t.Fatal(err)
   814  			}
   815  			out, err := yaml.JSONToYAML(buf.Bytes())
   816  			if err != nil {
   817  				t.Fatal(err)
   818  			}
   819  			if diff := cmp.Diff(tt.want, string(out)); diff != "" {
   820  				t.Errorf("unexpected encoded configuration:\n%s", diff)
   821  			}
   822  		})
   823  	}
   824  }
   825  

View as plain text