...

Source file src/k8s.io/kubernetes/pkg/controller/daemon/util/daemonset_util_test.go

Documentation: k8s.io/kubernetes/pkg/controller/daemon/util

     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 util
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"testing"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	extensions "k8s.io/api/extensions/v1beta1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    28  	"k8s.io/component-base/featuregate"
    29  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    30  	"k8s.io/utils/pointer"
    31  )
    32  
    33  func newPod(podName string, nodeName string, label map[string]string) *v1.Pod {
    34  	pod := &v1.Pod{
    35  		TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
    36  		ObjectMeta: metav1.ObjectMeta{
    37  			Labels:    label,
    38  			Namespace: metav1.NamespaceDefault,
    39  		},
    40  		Spec: v1.PodSpec{
    41  			NodeName: nodeName,
    42  			Containers: []v1.Container{
    43  				{
    44  					Image: "foo/bar",
    45  				},
    46  			},
    47  		},
    48  	}
    49  	pod.Name = podName
    50  	return pod
    51  }
    52  
    53  func TestIsPodUpdated(t *testing.T) {
    54  	templateGeneration := pointer.Int64(12345)
    55  	badGeneration := pointer.Int64(12350)
    56  	hash := "55555"
    57  	labels := map[string]string{extensions.DaemonSetTemplateGenerationKey: fmt.Sprint(*templateGeneration), extensions.DefaultDaemonSetUniqueLabelKey: hash}
    58  	labelsNoHash := map[string]string{extensions.DaemonSetTemplateGenerationKey: fmt.Sprint(*templateGeneration)}
    59  	tests := []struct {
    60  		test               string
    61  		templateGeneration *int64
    62  		pod                *v1.Pod
    63  		hash               string
    64  		isUpdated          bool
    65  	}{
    66  		{
    67  			"templateGeneration and hash both match",
    68  			templateGeneration,
    69  			newPod("pod1", "node1", labels),
    70  			hash,
    71  			true,
    72  		},
    73  		{
    74  			"templateGeneration matches, hash doesn't",
    75  			templateGeneration,
    76  			newPod("pod1", "node1", labels),
    77  			hash + "123",
    78  			true,
    79  		},
    80  		{
    81  			"templateGeneration matches, no hash label, has hash",
    82  			templateGeneration,
    83  			newPod("pod1", "node1", labelsNoHash),
    84  			hash,
    85  			true,
    86  		},
    87  		{
    88  			"templateGeneration matches, no hash label, no hash",
    89  			templateGeneration,
    90  			newPod("pod1", "node1", labelsNoHash),
    91  			"",
    92  			true,
    93  		},
    94  		{
    95  			"templateGeneration matches, has hash label, no hash",
    96  			templateGeneration,
    97  			newPod("pod1", "node1", labels),
    98  			"",
    99  			true,
   100  		},
   101  		{
   102  			"templateGeneration doesn't match, hash does",
   103  			badGeneration,
   104  			newPod("pod1", "node1", labels),
   105  			hash,
   106  			true,
   107  		},
   108  		{
   109  			"templateGeneration and hash don't match",
   110  			badGeneration,
   111  			newPod("pod1", "node1", labels),
   112  			hash + "123",
   113  			false,
   114  		},
   115  		{
   116  			"empty labels, no hash",
   117  			templateGeneration,
   118  			newPod("pod1", "node1", map[string]string{}),
   119  			"",
   120  			false,
   121  		},
   122  		{
   123  			"empty labels",
   124  			templateGeneration,
   125  			newPod("pod1", "node1", map[string]string{}),
   126  			hash,
   127  			false,
   128  		},
   129  		{
   130  			"no labels",
   131  			templateGeneration,
   132  			newPod("pod1", "node1", nil),
   133  			hash,
   134  			false,
   135  		},
   136  	}
   137  	for _, test := range tests {
   138  		updated := IsPodUpdated(test.pod, test.hash, test.templateGeneration)
   139  		if updated != test.isUpdated {
   140  			t.Errorf("%s: IsPodUpdated returned wrong value. Expected %t, got %t", test.test, test.isUpdated, updated)
   141  		}
   142  	}
   143  }
   144  
   145  func TestCreatePodTemplate(t *testing.T) {
   146  	tests := []struct {
   147  		templateGeneration *int64
   148  		hash               string
   149  		expectUniqueLabel  bool
   150  	}{
   151  		{pointer.Int64(1), "", false},
   152  		{pointer.Int64(2), "3242341807", true},
   153  	}
   154  	for _, test := range tests {
   155  		podTemplateSpec := v1.PodTemplateSpec{}
   156  		newPodTemplate := CreatePodTemplate(podTemplateSpec, test.templateGeneration, test.hash)
   157  		val, exists := newPodTemplate.ObjectMeta.Labels[extensions.DaemonSetTemplateGenerationKey]
   158  		if !exists || val != fmt.Sprint(*test.templateGeneration) {
   159  			t.Errorf("Expected podTemplateSpec to have generation label value: %d, got: %s", *test.templateGeneration, val)
   160  		}
   161  		val, exists = newPodTemplate.ObjectMeta.Labels[extensions.DefaultDaemonSetUniqueLabelKey]
   162  		if test.expectUniqueLabel && (!exists || val != test.hash) {
   163  			t.Errorf("Expected podTemplateSpec to have hash label value: %s, got: %s", test.hash, val)
   164  		}
   165  		if !test.expectUniqueLabel && exists {
   166  			t.Errorf("Expected podTemplateSpec to have no hash label, got: %s", val)
   167  		}
   168  	}
   169  }
   170  
   171  func TestReplaceDaemonSetPodNodeNameNodeAffinity(t *testing.T) {
   172  	tests := []struct {
   173  		affinity *v1.Affinity
   174  		hostname string
   175  		expected *v1.Affinity
   176  	}{
   177  		{
   178  			affinity: nil,
   179  			hostname: "host_1",
   180  			expected: &v1.Affinity{
   181  				NodeAffinity: &v1.NodeAffinity{
   182  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   183  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   184  							{
   185  								MatchFields: []v1.NodeSelectorRequirement{
   186  									{
   187  										Key:      metav1.ObjectNameField,
   188  										Operator: v1.NodeSelectorOpIn,
   189  										Values:   []string{"host_1"},
   190  									},
   191  								},
   192  							},
   193  						},
   194  					},
   195  				},
   196  			},
   197  		},
   198  		{
   199  			affinity: &v1.Affinity{
   200  				NodeAffinity: &v1.NodeAffinity{
   201  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   202  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   203  							{
   204  								MatchExpressions: []v1.NodeSelectorRequirement{
   205  									{
   206  										Key:      v1.LabelHostname,
   207  										Operator: v1.NodeSelectorOpIn,
   208  										Values:   []string{"host_1"},
   209  									},
   210  								},
   211  							},
   212  						},
   213  					},
   214  				},
   215  			},
   216  			hostname: "host_1",
   217  			expected: &v1.Affinity{
   218  				NodeAffinity: &v1.NodeAffinity{
   219  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   220  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   221  							{
   222  								MatchFields: []v1.NodeSelectorRequirement{
   223  									{
   224  										Key:      metav1.ObjectNameField,
   225  										Operator: v1.NodeSelectorOpIn,
   226  										Values:   []string{"host_1"},
   227  									},
   228  								},
   229  							},
   230  						},
   231  					},
   232  				},
   233  			},
   234  		},
   235  		{
   236  			affinity: &v1.Affinity{
   237  				NodeAffinity: &v1.NodeAffinity{
   238  					PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{
   239  						{
   240  							Preference: v1.NodeSelectorTerm{
   241  								MatchExpressions: []v1.NodeSelectorRequirement{
   242  									{
   243  										Key:      v1.LabelHostname,
   244  										Operator: v1.NodeSelectorOpIn,
   245  										Values:   []string{"host_1"},
   246  									},
   247  								},
   248  							},
   249  						},
   250  					},
   251  				},
   252  			},
   253  			hostname: "host_1",
   254  			expected: &v1.Affinity{
   255  				NodeAffinity: &v1.NodeAffinity{
   256  					PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{
   257  						{
   258  							Preference: v1.NodeSelectorTerm{
   259  								MatchExpressions: []v1.NodeSelectorRequirement{
   260  									{
   261  										Key:      v1.LabelHostname,
   262  										Operator: v1.NodeSelectorOpIn,
   263  										Values:   []string{"host_1"},
   264  									},
   265  								},
   266  							},
   267  						},
   268  					},
   269  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   270  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   271  							{
   272  								MatchFields: []v1.NodeSelectorRequirement{
   273  									{
   274  										Key:      metav1.ObjectNameField,
   275  										Operator: v1.NodeSelectorOpIn,
   276  										Values:   []string{"host_1"},
   277  									},
   278  								},
   279  							},
   280  						},
   281  					},
   282  				},
   283  			},
   284  		},
   285  		{
   286  			affinity: &v1.Affinity{
   287  				NodeAffinity: &v1.NodeAffinity{
   288  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   289  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   290  							{
   291  								MatchFields: []v1.NodeSelectorRequirement{
   292  									{
   293  										Key:      metav1.ObjectNameField,
   294  										Operator: v1.NodeSelectorOpIn,
   295  										Values:   []string{"host_1", "host_2"},
   296  									},
   297  								},
   298  							},
   299  						},
   300  					},
   301  				},
   302  			},
   303  			hostname: "host_1",
   304  			expected: &v1.Affinity{
   305  				NodeAffinity: &v1.NodeAffinity{
   306  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   307  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   308  							{
   309  								MatchFields: []v1.NodeSelectorRequirement{
   310  									{
   311  										Key:      metav1.ObjectNameField,
   312  										Operator: v1.NodeSelectorOpIn,
   313  										Values:   []string{"host_1"},
   314  									},
   315  								},
   316  							},
   317  						},
   318  					},
   319  				},
   320  			},
   321  		},
   322  		{
   323  			affinity: nil,
   324  			hostname: "host_1",
   325  			expected: &v1.Affinity{
   326  				NodeAffinity: &v1.NodeAffinity{
   327  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   328  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   329  							{
   330  								MatchFields: []v1.NodeSelectorRequirement{
   331  									{
   332  										Key:      metav1.ObjectNameField,
   333  										Operator: v1.NodeSelectorOpIn,
   334  										Values:   []string{"host_1"},
   335  									},
   336  								},
   337  							},
   338  						},
   339  					},
   340  				},
   341  			},
   342  		},
   343  		{
   344  			affinity: &v1.Affinity{
   345  				NodeAffinity: &v1.NodeAffinity{
   346  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   347  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   348  							{
   349  								MatchExpressions: []v1.NodeSelectorRequirement{
   350  									{
   351  										Key:      "hostname",
   352  										Operator: v1.NodeSelectorOpIn,
   353  										Values:   []string{"host_1"},
   354  									},
   355  								},
   356  							},
   357  							{
   358  								MatchFields: []v1.NodeSelectorRequirement{
   359  									{
   360  										Key:      metav1.ObjectNameField,
   361  										Operator: v1.NodeSelectorOpIn,
   362  										Values:   []string{"host_2"},
   363  									},
   364  								},
   365  							},
   366  						},
   367  					},
   368  				},
   369  			},
   370  			hostname: "host_1",
   371  			expected: &v1.Affinity{
   372  				NodeAffinity: &v1.NodeAffinity{
   373  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   374  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   375  							{
   376  								MatchFields: []v1.NodeSelectorRequirement{
   377  									{
   378  										Key:      metav1.ObjectNameField,
   379  										Operator: v1.NodeSelectorOpIn,
   380  										Values:   []string{"host_1"},
   381  									},
   382  								},
   383  							},
   384  						},
   385  					},
   386  				},
   387  			},
   388  		},
   389  		{
   390  			affinity: &v1.Affinity{
   391  				NodeAffinity: &v1.NodeAffinity{
   392  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   393  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   394  							{
   395  								MatchFields: []v1.NodeSelectorRequirement{
   396  									{
   397  										Key:      metav1.ObjectNameField,
   398  										Operator: v1.NodeSelectorOpNotIn,
   399  										Values:   []string{"host_2"},
   400  									},
   401  								},
   402  							},
   403  						},
   404  					},
   405  				},
   406  			},
   407  			hostname: "host_1",
   408  			expected: &v1.Affinity{
   409  				NodeAffinity: &v1.NodeAffinity{
   410  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   411  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   412  							{
   413  								MatchFields: []v1.NodeSelectorRequirement{
   414  									{
   415  										Key:      metav1.ObjectNameField,
   416  										Operator: v1.NodeSelectorOpIn,
   417  										Values:   []string{"host_1"},
   418  									},
   419  								},
   420  							},
   421  						},
   422  					},
   423  				},
   424  			},
   425  		},
   426  		{
   427  			affinity: &v1.Affinity{
   428  				NodeAffinity: &v1.NodeAffinity{
   429  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   430  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   431  							{
   432  								MatchFields: []v1.NodeSelectorRequirement{
   433  									{
   434  										// NOTE: Only `metadata.name` is valid key in `MatchFields` in 1.11;
   435  										//       added this case for compatibility: the feature works as normal
   436  										//       when new Keys introduced.
   437  										Key:      "metadata.foo",
   438  										Operator: v1.NodeSelectorOpIn,
   439  										Values:   []string{"bar"},
   440  									},
   441  								},
   442  							},
   443  						},
   444  					},
   445  				},
   446  			},
   447  			hostname: "host_1",
   448  			expected: &v1.Affinity{
   449  				NodeAffinity: &v1.NodeAffinity{
   450  					RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   451  						NodeSelectorTerms: []v1.NodeSelectorTerm{
   452  							{
   453  								MatchFields: []v1.NodeSelectorRequirement{
   454  									{
   455  										Key:      metav1.ObjectNameField,
   456  										Operator: v1.NodeSelectorOpIn,
   457  										Values:   []string{"host_1"},
   458  									},
   459  								},
   460  							},
   461  						},
   462  					},
   463  				},
   464  			},
   465  		},
   466  	}
   467  
   468  	for i, test := range tests {
   469  		got := ReplaceDaemonSetPodNodeNameNodeAffinity(test.affinity, test.hostname)
   470  		if !reflect.DeepEqual(test.expected, got) {
   471  			t.Errorf("Failed to append NodeAffinity in case %d, got: %v, expected: %v",
   472  				i, got, test.expected)
   473  		}
   474  	}
   475  }
   476  
   477  func forEachFeatureGate(t *testing.T, tf func(t *testing.T), gates ...featuregate.Feature) {
   478  	for _, fg := range gates {
   479  		for _, f := range []bool{true, false} {
   480  			func() {
   481  				defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, fg, f)()
   482  				t.Run(fmt.Sprintf("%v (%t)", fg, f), tf)
   483  			}()
   484  		}
   485  	}
   486  }
   487  
   488  func TestGetTargetNodeName(t *testing.T) {
   489  	testFun := func(t *testing.T) {
   490  		tests := []struct {
   491  			pod         *v1.Pod
   492  			nodeName    string
   493  			expectedErr bool
   494  		}{
   495  			{
   496  				pod: &v1.Pod{
   497  					ObjectMeta: metav1.ObjectMeta{
   498  						Name:      "pod1",
   499  						Namespace: "default",
   500  					},
   501  					Spec: v1.PodSpec{
   502  						NodeName: "node-1",
   503  					},
   504  				},
   505  				nodeName: "node-1",
   506  			},
   507  			{
   508  				pod: &v1.Pod{
   509  					ObjectMeta: metav1.ObjectMeta{
   510  						Name:      "pod2",
   511  						Namespace: "default",
   512  					},
   513  					Spec: v1.PodSpec{
   514  						Affinity: &v1.Affinity{
   515  							NodeAffinity: &v1.NodeAffinity{
   516  								RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   517  									NodeSelectorTerms: []v1.NodeSelectorTerm{
   518  										{
   519  											MatchFields: []v1.NodeSelectorRequirement{
   520  												{
   521  													Key:      metav1.ObjectNameField,
   522  													Operator: v1.NodeSelectorOpIn,
   523  													Values:   []string{"node-1"},
   524  												},
   525  											},
   526  										},
   527  									},
   528  								},
   529  							},
   530  						},
   531  					},
   532  				},
   533  				nodeName: "node-1",
   534  			},
   535  			{
   536  				pod: &v1.Pod{
   537  					ObjectMeta: metav1.ObjectMeta{
   538  						Name:      "pod3",
   539  						Namespace: "default",
   540  					},
   541  					Spec: v1.PodSpec{
   542  						Affinity: &v1.Affinity{
   543  							NodeAffinity: &v1.NodeAffinity{
   544  								RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
   545  									NodeSelectorTerms: []v1.NodeSelectorTerm{
   546  										{
   547  											MatchFields: []v1.NodeSelectorRequirement{
   548  												{
   549  													Key:      metav1.ObjectNameField,
   550  													Operator: v1.NodeSelectorOpIn,
   551  													Values:   []string{"node-1", "node-2"},
   552  												},
   553  											},
   554  										},
   555  									},
   556  								},
   557  							},
   558  						},
   559  					},
   560  				},
   561  				expectedErr: true,
   562  			},
   563  			{
   564  				pod: &v1.Pod{
   565  					ObjectMeta: metav1.ObjectMeta{
   566  						Name:      "pod4",
   567  						Namespace: "default",
   568  					},
   569  					Spec: v1.PodSpec{},
   570  				},
   571  				expectedErr: true,
   572  			},
   573  		}
   574  
   575  		for _, test := range tests {
   576  			got, err := GetTargetNodeName(test.pod)
   577  			if test.expectedErr != (err != nil) {
   578  				t.Errorf("Unexpected error, expectedErr: %v, err: %v", test.expectedErr, err)
   579  			} else if !test.expectedErr {
   580  				if test.nodeName != got {
   581  					t.Errorf("Failed to get target node name, got: %v, expected: %v", got, test.nodeName)
   582  				}
   583  			}
   584  		}
   585  	}
   586  
   587  	forEachFeatureGate(t, testFun)
   588  }
   589  

View as plain text