2017-02-16 10:18:16 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-03-08 02:09:54 +00:00
|
|
|
"reflect"
|
2017-02-16 10:18:16 +00:00
|
|
|
"testing"
|
|
|
|
|
2017-06-22 17:25:57 +00:00
|
|
|
"k8s.io/api/core/v1"
|
2017-06-22 18:04:37 +00:00
|
|
|
extensions "k8s.io/api/extensions/v1beta1"
|
2017-06-22 18:24:23 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2018-06-02 00:38:58 +00:00
|
|
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
2018-11-21 05:25:58 +00:00
|
|
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
2018-06-02 00:38:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/features"
|
2018-09-28 02:37:38 +00:00
|
|
|
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
|
2017-02-16 10:18:16 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func newPod(podName string, nodeName string, label map[string]string) *v1.Pod {
|
|
|
|
pod := &v1.Pod{
|
2019-01-29 14:57:12 +00:00
|
|
|
TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
|
2017-02-16 10:18:16 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Labels: label,
|
|
|
|
Namespace: metav1.NamespaceDefault,
|
|
|
|
},
|
|
|
|
Spec: v1.PodSpec{
|
|
|
|
NodeName: nodeName,
|
|
|
|
Containers: []v1.Container{
|
|
|
|
{
|
|
|
|
Image: "foo/bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
pod.Name = podName
|
|
|
|
return pod
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestIsPodUpdated(t *testing.T) {
|
2018-02-14 18:35:38 +00:00
|
|
|
templateGeneration := int64Ptr(12345)
|
|
|
|
badGeneration := int64Ptr(12345)
|
2017-05-17 23:53:46 +00:00
|
|
|
hash := "55555"
|
|
|
|
labels := map[string]string{extensions.DaemonSetTemplateGenerationKey: fmt.Sprint(templateGeneration), extensions.DefaultDaemonSetUniqueLabelKey: hash}
|
|
|
|
labelsNoHash := map[string]string{extensions.DaemonSetTemplateGenerationKey: fmt.Sprint(templateGeneration)}
|
2017-02-16 10:18:16 +00:00
|
|
|
tests := []struct {
|
2017-05-17 23:53:46 +00:00
|
|
|
test string
|
2018-02-14 18:35:38 +00:00
|
|
|
templateGeneration *int64
|
2017-02-16 10:18:16 +00:00
|
|
|
pod *v1.Pod
|
2017-05-17 23:53:46 +00:00
|
|
|
hash string
|
2017-02-16 10:18:16 +00:00
|
|
|
isUpdated bool
|
|
|
|
}{
|
|
|
|
{
|
2017-05-17 23:53:46 +00:00
|
|
|
"templateGeneration and hash both match",
|
|
|
|
templateGeneration,
|
|
|
|
newPod("pod1", "node1", labels),
|
|
|
|
hash,
|
2017-02-16 10:18:16 +00:00
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
2017-05-17 23:53:46 +00:00
|
|
|
"templateGeneration matches, hash doesn't",
|
|
|
|
templateGeneration,
|
|
|
|
newPod("pod1", "node1", labels),
|
|
|
|
hash + "123",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"templateGeneration matches, no hash label, has hash",
|
|
|
|
templateGeneration,
|
|
|
|
newPod("pod1", "node1", labelsNoHash),
|
|
|
|
hash,
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"templateGeneration matches, no hash label, no hash",
|
|
|
|
templateGeneration,
|
|
|
|
newPod("pod1", "node1", labelsNoHash),
|
|
|
|
"",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"templateGeneration matches, has hash label, no hash",
|
|
|
|
templateGeneration,
|
|
|
|
newPod("pod1", "node1", labels),
|
|
|
|
"",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"templateGeneration doesn't match, hash does",
|
2018-02-14 18:35:38 +00:00
|
|
|
badGeneration,
|
2017-05-17 23:53:46 +00:00
|
|
|
newPod("pod1", "node1", labels),
|
|
|
|
hash,
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"templateGeneration and hash don't match",
|
2018-02-14 18:35:38 +00:00
|
|
|
badGeneration,
|
2017-05-17 23:53:46 +00:00
|
|
|
newPod("pod1", "node1", labels),
|
|
|
|
hash + "123",
|
2017-02-16 10:18:16 +00:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
2017-05-17 23:53:46 +00:00
|
|
|
"empty labels, no hash",
|
|
|
|
templateGeneration,
|
2017-02-16 10:18:16 +00:00
|
|
|
newPod("pod1", "node1", map[string]string{}),
|
2017-05-17 23:53:46 +00:00
|
|
|
"",
|
2017-02-16 10:18:16 +00:00
|
|
|
false,
|
|
|
|
},
|
2017-02-23 23:21:56 +00:00
|
|
|
{
|
2017-05-17 23:53:46 +00:00
|
|
|
"empty labels",
|
|
|
|
templateGeneration,
|
|
|
|
newPod("pod1", "node1", map[string]string{}),
|
|
|
|
hash,
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"no labels",
|
|
|
|
templateGeneration,
|
2017-02-23 23:21:56 +00:00
|
|
|
newPod("pod1", "node1", nil),
|
2017-05-17 23:53:46 +00:00
|
|
|
hash,
|
2017-02-23 23:21:56 +00:00
|
|
|
false,
|
|
|
|
},
|
2017-02-16 10:18:16 +00:00
|
|
|
}
|
|
|
|
for _, test := range tests {
|
2018-02-14 18:35:38 +00:00
|
|
|
updated := IsPodUpdated(test.pod, test.hash, test.templateGeneration)
|
2017-02-16 10:18:16 +00:00
|
|
|
if updated != test.isUpdated {
|
2017-05-17 23:53:46 +00:00
|
|
|
t.Errorf("%s: IsPodUpdated returned wrong value. Expected %t, got %t", test.test, test.isUpdated, updated)
|
2017-02-16 10:18:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 23:53:46 +00:00
|
|
|
func TestCreatePodTemplate(t *testing.T) {
|
|
|
|
tests := []struct {
|
2018-02-14 18:35:38 +00:00
|
|
|
templateGeneration *int64
|
2017-05-17 23:53:46 +00:00
|
|
|
hash string
|
|
|
|
expectUniqueLabel bool
|
|
|
|
}{
|
2018-02-14 18:35:38 +00:00
|
|
|
{int64Ptr(1), "", false},
|
|
|
|
{int64Ptr(2), "3242341807", true},
|
2017-05-17 23:53:46 +00:00
|
|
|
}
|
|
|
|
for _, test := range tests {
|
|
|
|
podTemplateSpec := v1.PodTemplateSpec{}
|
2018-11-29 09:18:32 +00:00
|
|
|
newPodTemplate := CreatePodTemplate(podTemplateSpec, test.templateGeneration, test.hash)
|
2017-05-17 23:53:46 +00:00
|
|
|
val, exists := newPodTemplate.ObjectMeta.Labels[extensions.DaemonSetTemplateGenerationKey]
|
2018-02-14 18:35:38 +00:00
|
|
|
if !exists || val != fmt.Sprint(*test.templateGeneration) {
|
|
|
|
t.Errorf("Expected podTemplateSpec to have generation label value: %d, got: %s", *test.templateGeneration, val)
|
2017-05-17 23:53:46 +00:00
|
|
|
}
|
|
|
|
val, exists = newPodTemplate.ObjectMeta.Labels[extensions.DefaultDaemonSetUniqueLabelKey]
|
|
|
|
if test.expectUniqueLabel && (!exists || val != test.hash) {
|
|
|
|
t.Errorf("Expected podTemplateSpec to have hash label value: %s, got: %s", test.hash, val)
|
|
|
|
}
|
|
|
|
if !test.expectUniqueLabel && exists {
|
|
|
|
t.Errorf("Expected podTemplateSpec to have no hash label, got: %s", val)
|
|
|
|
}
|
2017-02-16 10:18:16 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-14 18:35:38 +00:00
|
|
|
|
|
|
|
func int64Ptr(i int) *int64 {
|
|
|
|
li := int64(i)
|
|
|
|
return &li
|
|
|
|
}
|
2018-03-08 02:09:54 +00:00
|
|
|
|
2018-06-02 00:38:58 +00:00
|
|
|
func TestReplaceDaemonSetPodNodeNameNodeAffinity(t *testing.T) {
|
2018-03-08 02:09:54 +00:00
|
|
|
tests := []struct {
|
|
|
|
affinity *v1.Affinity
|
|
|
|
hostname string
|
|
|
|
expected *v1.Affinity
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
affinity: nil,
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
2018-06-02 00:38:58 +00:00
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: &v1.Affinity{
|
2018-03-08 02:09:54 +00:00
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2019-02-01 02:43:49 +00:00
|
|
|
Key: v1.LabelHostname,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-06-02 00:38:58 +00:00
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-03-08 02:09:54 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{
|
|
|
|
{
|
|
|
|
Preference: v1.NodeSelectorTerm{
|
|
|
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2019-02-01 02:43:49 +00:00
|
|
|
Key: v1.LabelHostname,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{
|
|
|
|
{
|
|
|
|
Preference: v1.NodeSelectorTerm{
|
|
|
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2019-02-01 02:43:49 +00:00
|
|
|
Key: v1.LabelHostname,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
2018-06-02 00:38:58 +00:00
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
2018-03-08 02:09:54 +00:00
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
2018-06-02 00:38:58 +00:00
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
2018-03-08 02:09:54 +00:00
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
2018-06-02 00:38:58 +00:00
|
|
|
Values: []string{"host_1", "host_2"},
|
2018-03-08 02:09:54 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
2018-06-02 00:38:58 +00:00
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
2018-03-08 02:09:54 +00:00
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
2018-06-02 00:38:58 +00:00
|
|
|
Values: []string{"host_1"},
|
2018-03-08 02:09:54 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-06-02 00:38:58 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: nil,
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
2018-03-08 02:09:54 +00:00
|
|
|
{
|
2018-06-02 00:38:58 +00:00
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
2018-03-08 02:09:54 +00:00
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-06-02 00:38:58 +00:00
|
|
|
Key: "hostname",
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
2018-06-02 00:38:58 +00:00
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_2"},
|
2018-03-08 02:09:54 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
2018-06-02 00:38:58 +00:00
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
2018-03-08 02:09:54 +00:00
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpNotIn,
|
|
|
|
Values: []string{"host_2"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
|
|
|
// NOTE: Only `metadata.name` is valid key in `MatchFields` in 1.11;
|
|
|
|
// added this case for compatibility: the feature works as normal
|
|
|
|
// when new Keys introduced.
|
|
|
|
Key: "metadata.foo",
|
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"bar"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
hostname: "host_1",
|
|
|
|
expected: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-03-08 02:09:54 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"host_1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-06-02 00:38:58 +00:00
|
|
|
for i, test := range tests {
|
|
|
|
got := ReplaceDaemonSetPodNodeNameNodeAffinity(test.affinity, test.hostname)
|
2018-03-08 02:09:54 +00:00
|
|
|
if !reflect.DeepEqual(test.expected, got) {
|
2018-06-02 00:38:58 +00:00
|
|
|
t.Errorf("Failed to append NodeAffinity in case %d, got: %v, expected: %v",
|
|
|
|
i, got, test.expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func forEachFeatureGate(t *testing.T, tf func(t *testing.T), gates ...utilfeature.Feature) {
|
|
|
|
for _, fg := range gates {
|
2018-11-21 05:25:58 +00:00
|
|
|
for _, f := range []bool{true, false} {
|
|
|
|
func() {
|
|
|
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, fg, f)()
|
2018-06-02 00:38:58 +00:00
|
|
|
t.Run(fmt.Sprintf("%v (%t)", fg, f), tf)
|
2018-11-21 05:25:58 +00:00
|
|
|
}()
|
|
|
|
}
|
2018-06-02 00:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetTargetNodeName(t *testing.T) {
|
|
|
|
testFun := func(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
pod *v1.Pod
|
|
|
|
nodeName string
|
|
|
|
expectedErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
pod: &v1.Pod{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "pod1",
|
|
|
|
Namespace: "default",
|
|
|
|
},
|
|
|
|
Spec: v1.PodSpec{
|
|
|
|
NodeName: "node-1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nodeName: "node-1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: &v1.Pod{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "pod2",
|
|
|
|
Namespace: "default",
|
|
|
|
},
|
|
|
|
Spec: v1.PodSpec{
|
|
|
|
Affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"node-1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nodeName: "node-1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: &v1.Pod{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "pod3",
|
|
|
|
Namespace: "default",
|
|
|
|
},
|
|
|
|
Spec: v1.PodSpec{
|
|
|
|
Affinity: &v1.Affinity{
|
|
|
|
NodeAffinity: &v1.NodeAffinity{
|
|
|
|
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
|
|
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
|
|
|
{
|
|
|
|
MatchFields: []v1.NodeSelectorRequirement{
|
|
|
|
{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.NodeFieldSelectorKeyNodeName,
|
2018-06-02 00:38:58 +00:00
|
|
|
Operator: v1.NodeSelectorOpIn,
|
|
|
|
Values: []string{"node-1", "node-2"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: &v1.Pod{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "pod4",
|
|
|
|
Namespace: "default",
|
|
|
|
},
|
|
|
|
Spec: v1.PodSpec{},
|
|
|
|
},
|
|
|
|
expectedErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
got, err := GetTargetNodeName(test.pod)
|
|
|
|
if test.expectedErr != (err != nil) {
|
|
|
|
t.Errorf("Unexpected error, expectedErr: %v, err: %v", test.expectedErr, err)
|
|
|
|
} else if !test.expectedErr {
|
|
|
|
if test.nodeName != got {
|
|
|
|
t.Errorf("Failed to get target node name, got: %v, expected: %v", got, test.nodeName)
|
|
|
|
}
|
|
|
|
}
|
2018-03-08 02:09:54 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-02 00:38:58 +00:00
|
|
|
|
|
|
|
forEachFeatureGate(t, testFun, features.ScheduleDaemonSetPods)
|
2018-03-08 02:09:54 +00:00
|
|
|
}
|