
Prepatory work fpr removing cloud provider dependency from node controller running in Kube Controller Manager. Splitting the node controller into its two major pieces life-cycle and CIDR/IP management. Both pieces currently need the the cloud system to do their work. Removing lifecycles dependency on cloud will be fixed ina followup PR. Moved node scheduler code to live with node lifecycle controller. Got the IPAM/Lifecycle split completed. Still need to rename pieces. Made changes to the utils and tests so they would be in the appropriate package. Moved the node based ipam code to nodeipam. Made the relevant tests pass. Moved common node controller util code to nodeutil. Removed unneeded pod informer sync from node ipam controller. Fixed linter issues. Factored in feedback from @gmarek. Factored in feedback from @mtaufen. Undoing unneeded change.
605 lines
18 KiB
Go
605 lines
18 KiB
Go
/*
|
|
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 scheduler
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"k8s.io/api/core/v1"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
"k8s.io/kubernetes/pkg/controller/testutil"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
clienttesting "k8s.io/client-go/testing"
|
|
)
|
|
|
|
var timeForControllerToProgress = 500 * time.Millisecond
|
|
|
|
func createNoExecuteTaint(index int) v1.Taint {
|
|
now := metav1.Now()
|
|
return v1.Taint{
|
|
Key: "testTaint" + fmt.Sprintf("%v", index),
|
|
Value: "test" + fmt.Sprintf("%v", index),
|
|
Effect: v1.TaintEffectNoExecute,
|
|
TimeAdded: &now,
|
|
}
|
|
}
|
|
|
|
func addToleration(pod *v1.Pod, index int, duration int64) *v1.Pod {
|
|
if pod.Annotations == nil {
|
|
pod.Annotations = map[string]string{}
|
|
}
|
|
if duration < 0 {
|
|
pod.Spec.Tolerations = []v1.Toleration{{Key: "testTaint" + fmt.Sprintf("%v", index), Value: "test" + fmt.Sprintf("%v", index), Effect: v1.TaintEffectNoExecute}}
|
|
|
|
} else {
|
|
pod.Spec.Tolerations = []v1.Toleration{{Key: "testTaint" + fmt.Sprintf("%v", index), Value: "test" + fmt.Sprintf("%v", index), Effect: v1.TaintEffectNoExecute, TolerationSeconds: &duration}}
|
|
}
|
|
return pod
|
|
}
|
|
|
|
func addTaintsToNode(node *v1.Node, key, value string, indices []int) *v1.Node {
|
|
taints := []v1.Taint{}
|
|
for _, index := range indices {
|
|
taints = append(taints, createNoExecuteTaint(index))
|
|
}
|
|
node.Spec.Taints = taints
|
|
return node
|
|
}
|
|
|
|
type timestampedPod struct {
|
|
names []string
|
|
timestamp time.Duration
|
|
}
|
|
|
|
type durationSlice []timestampedPod
|
|
|
|
func (a durationSlice) Len() int { return len(a) }
|
|
func (a durationSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a durationSlice) Less(i, j int) bool { return a[i].timestamp < a[j].timestamp }
|
|
|
|
func TestFilterNoExecuteTaints(t *testing.T) {
|
|
taints := []v1.Taint{
|
|
{
|
|
Key: "one",
|
|
Value: "one",
|
|
Effect: v1.TaintEffectNoExecute,
|
|
},
|
|
{
|
|
Key: "two",
|
|
Value: "two",
|
|
Effect: v1.TaintEffectNoSchedule,
|
|
},
|
|
}
|
|
taints = getNoExecuteTaints(taints)
|
|
if len(taints) != 1 || taints[0].Key != "one" {
|
|
t.Errorf("Filtering doesn't work. Got %v", taints)
|
|
}
|
|
}
|
|
|
|
func TestCreatePod(t *testing.T) {
|
|
testCases := []struct {
|
|
description string
|
|
pod *v1.Pod
|
|
taintedNodes map[string][]v1.Taint
|
|
expectDelete bool
|
|
}{
|
|
{
|
|
description: "not scheduled - ignore",
|
|
pod: testutil.NewPod("pod1", ""),
|
|
taintedNodes: map[string][]v1.Taint{},
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "scheduled on untainted Node",
|
|
pod: testutil.NewPod("pod1", "node1"),
|
|
taintedNodes: map[string][]v1.Taint{},
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "schedule on tainted Node",
|
|
pod: testutil.NewPod("pod1", "node1"),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: true,
|
|
},
|
|
{
|
|
description: "schedule on tainted Node with finite toleration",
|
|
pod: addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "schedule on tainted Node with infinite toleration",
|
|
pod: addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "schedule on tainted Node with infinite ivalid toleration",
|
|
pod: addToleration(testutil.NewPod("pod1", "node1"), 2, -1),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: true,
|
|
},
|
|
}
|
|
|
|
for _, item := range testCases {
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset()
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
go controller.Run(stopCh)
|
|
controller.taintedNodes = item.taintedNodes
|
|
controller.PodUpdated(nil, item.pod)
|
|
// wait a bit
|
|
time.Sleep(timeForControllerToProgress)
|
|
|
|
podDeleted := false
|
|
for _, action := range fakeClientset.Actions() {
|
|
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
|
|
podDeleted = true
|
|
}
|
|
}
|
|
if podDeleted != item.expectDelete {
|
|
t.Errorf("%v: Unexepected test result. Expected delete %v, got %v", item.description, item.expectDelete, podDeleted)
|
|
}
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestDeletePod(t *testing.T) {
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset()
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
go controller.Run(stopCh)
|
|
controller.taintedNodes = map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
}
|
|
controller.PodUpdated(testutil.NewPod("pod1", "node1"), nil)
|
|
// wait a bit to see if nothing will panic
|
|
time.Sleep(timeForControllerToProgress)
|
|
close(stopCh)
|
|
}
|
|
|
|
func TestUpdatePod(t *testing.T) {
|
|
testCases := []struct {
|
|
description string
|
|
prevPod *v1.Pod
|
|
newPod *v1.Pod
|
|
taintedNodes map[string][]v1.Taint
|
|
expectDelete bool
|
|
additionalSleep time.Duration
|
|
}{
|
|
{
|
|
description: "scheduling onto tainted Node",
|
|
prevPod: testutil.NewPod("pod1", ""),
|
|
newPod: testutil.NewPod("pod1", "node1"),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: true,
|
|
},
|
|
{
|
|
description: "scheduling onto tainted Node with toleration",
|
|
prevPod: addToleration(testutil.NewPod("pod1", ""), 1, -1),
|
|
newPod: addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "removing toleration",
|
|
prevPod: addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
|
newPod: testutil.NewPod("pod1", "node1"),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: true,
|
|
},
|
|
{
|
|
description: "lengthening toleration shouldn't work",
|
|
prevPod: addToleration(testutil.NewPod("pod1", "node1"), 1, 1),
|
|
newPod: addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
|
taintedNodes: map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
},
|
|
expectDelete: true,
|
|
additionalSleep: 1500 * time.Millisecond,
|
|
},
|
|
}
|
|
|
|
for _, item := range testCases {
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset()
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
go controller.Run(stopCh)
|
|
controller.taintedNodes = item.taintedNodes
|
|
|
|
controller.PodUpdated(nil, item.prevPod)
|
|
fakeClientset.ClearActions()
|
|
time.Sleep(timeForControllerToProgress)
|
|
controller.PodUpdated(item.prevPod, item.newPod)
|
|
// wait a bit
|
|
time.Sleep(timeForControllerToProgress)
|
|
if item.additionalSleep > 0 {
|
|
time.Sleep(item.additionalSleep)
|
|
}
|
|
|
|
podDeleted := false
|
|
for _, action := range fakeClientset.Actions() {
|
|
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
|
|
podDeleted = true
|
|
}
|
|
}
|
|
if podDeleted != item.expectDelete {
|
|
t.Errorf("%v: Unexepected test result. Expected delete %v, got %v", item.description, item.expectDelete, podDeleted)
|
|
}
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestCreateNode(t *testing.T) {
|
|
testCases := []struct {
|
|
description string
|
|
pods []v1.Pod
|
|
node *v1.Node
|
|
expectDelete bool
|
|
}{
|
|
{
|
|
description: "Creating Node maching already assigned Pod",
|
|
pods: []v1.Pod{
|
|
*testutil.NewPod("pod1", "node1"),
|
|
},
|
|
node: testutil.NewNode("node1"),
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "Creating tainted Node maching already assigned Pod",
|
|
pods: []v1.Pod{
|
|
*testutil.NewPod("pod1", "node1"),
|
|
},
|
|
node: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
expectDelete: true,
|
|
},
|
|
{
|
|
description: "Creating tainted Node maching already assigned tolerating Pod",
|
|
pods: []v1.Pod{
|
|
*addToleration(testutil.NewPod("pod1", "node1"), 1, -1),
|
|
},
|
|
node: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
expectDelete: false,
|
|
},
|
|
}
|
|
|
|
for _, item := range testCases {
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
go controller.Run(stopCh)
|
|
controller.NodeUpdated(nil, item.node)
|
|
// wait a bit
|
|
time.Sleep(timeForControllerToProgress)
|
|
|
|
podDeleted := false
|
|
for _, action := range fakeClientset.Actions() {
|
|
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
|
|
podDeleted = true
|
|
}
|
|
}
|
|
if podDeleted != item.expectDelete {
|
|
t.Errorf("%v: Unexepected test result. Expected delete %v, got %v", item.description, item.expectDelete, podDeleted)
|
|
}
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestDeleteNode(t *testing.T) {
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset()
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
controller.taintedNodes = map[string][]v1.Taint{
|
|
"node1": {createNoExecuteTaint(1)},
|
|
}
|
|
go controller.Run(stopCh)
|
|
controller.NodeUpdated(testutil.NewNode("node1"), nil)
|
|
// wait a bit to see if nothing will panic
|
|
time.Sleep(timeForControllerToProgress)
|
|
controller.taintedNodesLock.Lock()
|
|
if _, ok := controller.taintedNodes["node1"]; ok {
|
|
t.Error("Node should have been deleted from taintedNodes list")
|
|
}
|
|
controller.taintedNodesLock.Unlock()
|
|
close(stopCh)
|
|
}
|
|
|
|
func TestUpdateNode(t *testing.T) {
|
|
testCases := []struct {
|
|
description string
|
|
pods []v1.Pod
|
|
oldNode *v1.Node
|
|
newNode *v1.Node
|
|
expectDelete bool
|
|
additionalSleep time.Duration
|
|
}{
|
|
{
|
|
description: "Added taint",
|
|
pods: []v1.Pod{
|
|
*testutil.NewPod("pod1", "node1"),
|
|
},
|
|
oldNode: testutil.NewNode("node1"),
|
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
expectDelete: true,
|
|
},
|
|
{
|
|
description: "Added tolerated taint",
|
|
pods: []v1.Pod{
|
|
*addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
|
},
|
|
oldNode: testutil.NewNode("node1"),
|
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
expectDelete: false,
|
|
},
|
|
{
|
|
description: "Only one added taint tolerated",
|
|
pods: []v1.Pod{
|
|
*addToleration(testutil.NewPod("pod1", "node1"), 1, 100),
|
|
},
|
|
oldNode: testutil.NewNode("node1"),
|
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
|
expectDelete: true,
|
|
},
|
|
{
|
|
description: "Taint removed",
|
|
pods: []v1.Pod{
|
|
*addToleration(testutil.NewPod("pod1", "node1"), 1, 1),
|
|
},
|
|
oldNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
newNode: testutil.NewNode("node1"),
|
|
expectDelete: false,
|
|
additionalSleep: 1500 * time.Millisecond,
|
|
},
|
|
{
|
|
description: "Pod with multiple tolerations are evicted when first one runs out",
|
|
pods: []v1.Pod{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: "default",
|
|
Name: "pod1",
|
|
},
|
|
Spec: v1.PodSpec{
|
|
NodeName: "node1",
|
|
Tolerations: []v1.Toleration{
|
|
{Key: "testTaint1", Value: "test1", Effect: v1.TaintEffectNoExecute, TolerationSeconds: &[]int64{1}[0]},
|
|
{Key: "testTaint2", Value: "test2", Effect: v1.TaintEffectNoExecute, TolerationSeconds: &[]int64{100}[0]},
|
|
},
|
|
},
|
|
Status: v1.PodStatus{
|
|
Conditions: []v1.PodCondition{
|
|
{
|
|
Type: v1.PodReady,
|
|
Status: v1.ConditionTrue,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
oldNode: testutil.NewNode("node1"),
|
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
|
expectDelete: true,
|
|
additionalSleep: 1500 * time.Millisecond,
|
|
},
|
|
}
|
|
|
|
for _, item := range testCases {
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
go controller.Run(stopCh)
|
|
controller.NodeUpdated(item.oldNode, item.newNode)
|
|
// wait a bit
|
|
time.Sleep(timeForControllerToProgress)
|
|
if item.additionalSleep > 0 {
|
|
time.Sleep(item.additionalSleep)
|
|
}
|
|
|
|
podDeleted := false
|
|
for _, action := range fakeClientset.Actions() {
|
|
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
|
|
podDeleted = true
|
|
}
|
|
}
|
|
if podDeleted != item.expectDelete {
|
|
t.Errorf("%v: Unexepected test result. Expected delete %v, got %v", item.description, item.expectDelete, podDeleted)
|
|
}
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestUpdateNodeWithMultiplePods(t *testing.T) {
|
|
testCases := []struct {
|
|
description string
|
|
pods []v1.Pod
|
|
oldNode *v1.Node
|
|
newNode *v1.Node
|
|
expectedDeleteTimes durationSlice
|
|
}{
|
|
{
|
|
description: "Pods with different toleration times are evicted appropriately",
|
|
pods: []v1.Pod{
|
|
*testutil.NewPod("pod1", "node1"),
|
|
*addToleration(testutil.NewPod("pod2", "node1"), 1, 1),
|
|
*addToleration(testutil.NewPod("pod3", "node1"), 1, -1),
|
|
},
|
|
oldNode: testutil.NewNode("node1"),
|
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1}),
|
|
expectedDeleteTimes: durationSlice{
|
|
{[]string{"pod1"}, 0},
|
|
{[]string{"pod2"}, time.Second},
|
|
},
|
|
},
|
|
{
|
|
description: "Evict all pods not maching all taints instantly",
|
|
pods: []v1.Pod{
|
|
*testutil.NewPod("pod1", "node1"),
|
|
*addToleration(testutil.NewPod("pod2", "node1"), 1, 1),
|
|
*addToleration(testutil.NewPod("pod3", "node1"), 1, -1),
|
|
},
|
|
oldNode: testutil.NewNode("node1"),
|
|
newNode: addTaintsToNode(testutil.NewNode("node1"), "testTaint1", "taint1", []int{1, 2}),
|
|
expectedDeleteTimes: durationSlice{
|
|
{[]string{"pod1", "pod2", "pod3"}, 0},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, item := range testCases {
|
|
t.Logf("Starting testcase %q", item.description)
|
|
|
|
stopCh := make(chan struct{})
|
|
fakeClientset := fake.NewSimpleClientset(&v1.PodList{Items: item.pods})
|
|
sort.Sort(item.expectedDeleteTimes)
|
|
controller := NewNoExecuteTaintManager(fakeClientset)
|
|
controller.recorder = testutil.NewFakeRecorder()
|
|
go controller.Run(stopCh)
|
|
controller.NodeUpdated(item.oldNode, item.newNode)
|
|
|
|
startedAt := time.Now()
|
|
for i := range item.expectedDeleteTimes {
|
|
if i == 0 || item.expectedDeleteTimes[i-1].timestamp != item.expectedDeleteTimes[i].timestamp {
|
|
// compute a grace duration to give controller time to process updates. Choose big
|
|
// enough intervals in the test cases above to avoid flakes.
|
|
var increment time.Duration
|
|
if i == len(item.expectedDeleteTimes)-1 || item.expectedDeleteTimes[i+1].timestamp == item.expectedDeleteTimes[i].timestamp {
|
|
increment = 500 * time.Millisecond
|
|
} else {
|
|
increment = ((item.expectedDeleteTimes[i+1].timestamp - item.expectedDeleteTimes[i].timestamp) / time.Duration(2))
|
|
}
|
|
|
|
sleepTime := item.expectedDeleteTimes[i].timestamp - time.Since(startedAt) + increment
|
|
if sleepTime < 0 {
|
|
sleepTime = 0
|
|
}
|
|
t.Logf("Sleeping for %v", sleepTime)
|
|
time.Sleep(sleepTime)
|
|
}
|
|
|
|
for delay, podName := range item.expectedDeleteTimes[i].names {
|
|
deleted := false
|
|
for _, action := range fakeClientset.Actions() {
|
|
deleteAction, ok := action.(clienttesting.DeleteActionImpl)
|
|
if !ok {
|
|
t.Logf("Found not-delete action with verb %v. Ignoring.", action.GetVerb())
|
|
continue
|
|
}
|
|
if deleteAction.GetResource().Resource != "pods" {
|
|
continue
|
|
}
|
|
if podName == deleteAction.GetName() {
|
|
deleted = true
|
|
}
|
|
}
|
|
if !deleted {
|
|
t.Errorf("Failed to deleted pod %v after %v", podName, delay)
|
|
}
|
|
}
|
|
for _, action := range fakeClientset.Actions() {
|
|
deleteAction, ok := action.(clienttesting.DeleteActionImpl)
|
|
if !ok {
|
|
t.Logf("Found not-delete action with verb %v. Ignoring.", action.GetVerb())
|
|
continue
|
|
}
|
|
if deleteAction.GetResource().Resource != "pods" {
|
|
continue
|
|
}
|
|
deletedPodName := deleteAction.GetName()
|
|
expected := false
|
|
for _, podName := range item.expectedDeleteTimes[i].names {
|
|
if podName == deletedPodName {
|
|
expected = true
|
|
}
|
|
}
|
|
if !expected {
|
|
t.Errorf("Pod %v was deleted even though it shouldn't have", deletedPodName)
|
|
}
|
|
}
|
|
fakeClientset.ClearActions()
|
|
}
|
|
|
|
close(stopCh)
|
|
}
|
|
}
|
|
|
|
func TestGetMinTolerationTime(t *testing.T) {
|
|
one := int64(1)
|
|
oneSec := 1 * time.Second
|
|
|
|
tests := []struct {
|
|
tolerations []v1.Toleration
|
|
expected time.Duration
|
|
}{
|
|
{
|
|
tolerations: []v1.Toleration{},
|
|
expected: 0,
|
|
},
|
|
{
|
|
tolerations: []v1.Toleration{
|
|
{
|
|
TolerationSeconds: &one,
|
|
},
|
|
{
|
|
TolerationSeconds: nil,
|
|
},
|
|
},
|
|
expected: oneSec,
|
|
},
|
|
{
|
|
tolerations: []v1.Toleration{
|
|
{
|
|
TolerationSeconds: nil,
|
|
},
|
|
{
|
|
TolerationSeconds: &one,
|
|
},
|
|
},
|
|
expected: oneSec,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
got := getMinTolerationTime(test.tolerations)
|
|
if got != test.expected {
|
|
t.Errorf("Incorrect min toleration time: got %v, expected %v", got, test.expected)
|
|
}
|
|
}
|
|
}
|