mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-11-06 05:17:08 +00:00
1172 lines
44 KiB
Go
1172 lines
44 KiB
Go
/*
|
|
Copyright 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 evictionrequest
|
|
|
|
import (
|
|
"math/rand"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
lifecyclev1alpha1 "k8s.io/api/lifecycle/v1alpha1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
apimachinerytypes "k8s.io/apimachinery/pkg/types"
|
|
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
|
|
"k8s.io/utils/clock"
|
|
testing2 "k8s.io/utils/clock/testing"
|
|
"k8s.io/utils/ptr"
|
|
)
|
|
|
|
func TestExtractEvictionNameParts(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
evictionName string
|
|
expectedTargetType string
|
|
expectedCounter int
|
|
expectedNamePart string
|
|
expectedOk bool
|
|
}{
|
|
{
|
|
name: "empty_name",
|
|
evictionName: "",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "invalid(simple)",
|
|
evictionName: "pod",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "invalid(without_name)",
|
|
evictionName: "pod-1",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "invalid(without_counter)",
|
|
evictionName: "pod-name",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "invalid(bad_prefix)",
|
|
evictionName: "invalid-name-123546-bad-format",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "invalid(without_target_type)",
|
|
evictionName: "123546-bad-format",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "invalid(negative_counter)",
|
|
evictionName: "pod--1-abcdefg1254",
|
|
expectedTargetType: "",
|
|
expectedCounter: -1,
|
|
expectedNamePart: "",
|
|
expectedOk: false,
|
|
},
|
|
{
|
|
name: "valid_name(zero)",
|
|
evictionName: "pod-0-abcdefg1254",
|
|
expectedTargetType: "pod",
|
|
expectedCounter: 0,
|
|
expectedNamePart: "abcdefg1254",
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "valid_name",
|
|
evictionName: "pod-1-abcdefg1254",
|
|
expectedTargetType: "pod",
|
|
expectedCounter: 1,
|
|
expectedNamePart: "abcdefg1254",
|
|
expectedOk: true,
|
|
},
|
|
{
|
|
name: "valid_name_long",
|
|
evictionName: "persistentvolumeclaimlongname-01590-1456abc",
|
|
expectedTargetType: "persistentvolumeclaimlongname",
|
|
expectedCounter: 1590,
|
|
expectedNamePart: "1456abc",
|
|
expectedOk: true,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
targetType, counter, namePart, ok := extractEvictionNameParts(tc.evictionName)
|
|
if targetType != tc.expectedTargetType {
|
|
t.Errorf("got targetType %v, expected %v", targetType, tc.expectedTargetType)
|
|
}
|
|
if counter != tc.expectedCounter {
|
|
t.Errorf("got counter %v, expected %v", counter, tc.expectedCounter)
|
|
}
|
|
if namePart != tc.expectedNamePart {
|
|
t.Errorf("got namePart %v, expected %v", namePart, tc.expectedNamePart)
|
|
}
|
|
if ok != tc.expectedOk {
|
|
t.Errorf("got ok %v, expected %v", ok, tc.expectedOk)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvictionNameForCounter(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
target targetInfo
|
|
counter int
|
|
expectedName string
|
|
}{
|
|
{
|
|
name: "empty_target",
|
|
expectedName: "unknown-0-",
|
|
},
|
|
{
|
|
name: "generates_a_valid_name",
|
|
target: newTargetInfoForEviction(mkValidPodTarget("foo", "uid-1"), nil),
|
|
expectedName: "pod-0-foo",
|
|
},
|
|
{
|
|
name: "generates_a_valid_name_with_a_counter",
|
|
target: newTargetInfoForEviction(mkValidPodTarget("foo", "uid-1"), nil),
|
|
counter: 15,
|
|
expectedName: "pod-15-foo",
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := evictionNameForCounter(tc.target, tc.counter); got != tc.expectedName {
|
|
t.Errorf("got evictionNameForCounter %v, expected %v", got, tc.expectedName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewEvictionName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
target targetInfo
|
|
evictions []*lifecyclev1alpha1.Eviction
|
|
expectedName string
|
|
}{
|
|
{
|
|
name: "empty_target",
|
|
expectedName: "",
|
|
},
|
|
{
|
|
name: "picks_first_name",
|
|
target: newTargetInfoForEviction(mkValidPodTarget("foo", "uid-1"), nil),
|
|
expectedName: "pod-1-foo",
|
|
},
|
|
{
|
|
name: "picks_the_next_name",
|
|
target: newTargetInfoForEviction(mkValidPodTarget("foo", "uid-1"), nil),
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-0-foo", "foo", "uid-0"),
|
|
mkValidEviction("pod-1-foo", "foo", "uid-1"),
|
|
mkValidEviction("pod-3-foo", "foo", "uid-3"),
|
|
mkValidEviction("pod-4-foo", "foo", "uid-4"),
|
|
mkValidEviction("pod-7-foo", "foo", "uid-7"),
|
|
},
|
|
expectedName: "pod-8-foo",
|
|
},
|
|
{
|
|
name: "too_long_name",
|
|
target: newTargetInfoForEviction(mkValidPodTarget(strings.Repeat("a", 253), "uid-1"), nil),
|
|
expectedName: "pod-1-" + strings.Repeat("a", 247),
|
|
},
|
|
{
|
|
name: "too_long_name_with_eviction",
|
|
target: newTargetInfoForEviction(mkValidPodTarget(strings.Repeat("a", 252)+"b", "uid-2"), nil),
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-"+strings.Repeat("a", 247), strings.Repeat("a", 252)+"c", "uid-1"),
|
|
},
|
|
expectedName: "pod-2-" + strings.Repeat("a", 247),
|
|
},
|
|
{
|
|
name: "too_long_name_with_invalid_ending_prevention",
|
|
target: newTargetInfoForEviction(mkValidPodTarget(strings.Repeat("a", 245)+".-"+strings.Repeat("a", 6), "uid-1"), nil),
|
|
expectedName: "pod-1-" + strings.Repeat("a", 245),
|
|
},
|
|
{
|
|
name: "too_long_name_with_eviction_and_with_invalid_ending_prevention",
|
|
target: newTargetInfoForEviction(mkValidPodTarget(strings.Repeat("a", 245)+".-"+strings.Repeat("a", 5)+"b", "uid-2"), nil),
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-"+strings.Repeat("a", 245), strings.Repeat("a", 245)+".-"+strings.Repeat("a", 5)+"c", "uid-1"),
|
|
},
|
|
expectedName: "pod-2-" + strings.Repeat("a", 245),
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := newEvictionName(tc.target, tc.evictions); got != tc.expectedName {
|
|
t.Errorf("got newEvictionName %v, expected %v", got, tc.expectedName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompareEvictionNames(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nameA string
|
|
nameB string
|
|
expectedCmp int
|
|
}{
|
|
{
|
|
name: "basic_names_sorted_lexicographically",
|
|
nameA: "abc012",
|
|
nameB: "abc013",
|
|
expectedCmp: -1,
|
|
},
|
|
{
|
|
name: "basic_names_sorted_lexicographically(reverse)",
|
|
nameA: "abc013",
|
|
nameB: "abc012",
|
|
expectedCmp: 1,
|
|
},
|
|
{
|
|
name: "valid_eviction_name_is_preferred",
|
|
nameA: "pod-1-nginx",
|
|
nameB: "abc012",
|
|
expectedCmp: -1,
|
|
},
|
|
{
|
|
name: "valid_eviction_name_is_preferred(reverse)",
|
|
nameA: "abc012",
|
|
nameB: "pod-1-nginx",
|
|
expectedCmp: 1,
|
|
},
|
|
{
|
|
name: "compares_by_target_type_first",
|
|
nameA: "pod-2-nginx",
|
|
nameB: "pvc-1-appricot",
|
|
expectedCmp: -1,
|
|
},
|
|
{
|
|
name: "compares_by_target_type_first(reverse)",
|
|
nameA: "pvc-1-appricot",
|
|
nameB: "pod-2-nginx",
|
|
expectedCmp: 1,
|
|
},
|
|
|
|
{
|
|
name: "compares_by_name_second",
|
|
nameA: "pod-1-nginx4",
|
|
nameB: "pod-1-nginx5",
|
|
expectedCmp: -1,
|
|
},
|
|
{
|
|
name: "compares_by_name_second(reverse)",
|
|
nameA: "pod-1-nginx5",
|
|
nameB: "pod-1-nginx4",
|
|
expectedCmp: 1,
|
|
},
|
|
{
|
|
name: "compares_by_counter_last",
|
|
nameA: "pod-0-nginx",
|
|
nameB: "pod-1-nginx",
|
|
expectedCmp: -1,
|
|
},
|
|
{
|
|
name: "compares_by_counter_last(reverse)",
|
|
nameA: "pod-2-nginx",
|
|
nameB: "pod-1-nginx",
|
|
expectedCmp: 1,
|
|
},
|
|
{
|
|
name: "compares_by_counter_with_multiple_digits",
|
|
nameA: "pod-123-nginx",
|
|
nameB: "pod-34-nginx",
|
|
expectedCmp: 89,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := compareEvictionNames(tc.nameA, tc.nameB); got != tc.expectedCmp {
|
|
t.Errorf("got cmp %v, expected %v", got, tc.expectedCmp)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindRelevantEviction(t *testing.T) {
|
|
now := time.Now()
|
|
clock := testing2.NewFakeClock(now)
|
|
clockOlder := testing2.NewFakeClock(now.Add(-time.Hour))
|
|
clockOldest := testing2.NewFakeClock(now.Add(-time.Hour - time.Second))
|
|
tests := []struct {
|
|
name string
|
|
evictions []*lifecyclev1alpha1.Eviction
|
|
expectedEviction *lifecyclev1alpha1.Eviction
|
|
expectedShouldCreate bool
|
|
}{
|
|
{
|
|
name: "no_evictions",
|
|
expectedEviction: nil,
|
|
expectedShouldCreate: true,
|
|
},
|
|
{
|
|
name: "latest_succeeded_eviction",
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-foo", "foo", "uid-1"),
|
|
mkValidEviction("pod-2-foo", "foo", "uid-1", setCreationTimestamp(clockOlder)),
|
|
mkValidEviction("pod-3-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
mkValidEviction("pod-4-foo", "foo", "uid-1"),
|
|
mkValidEviction("pod-5-foo", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
mkValidEviction("pod-6-foo", "foo", "uid-1",
|
|
addConditionTrue(clockOldest, lifecyclev1alpha1.EvictionConditionTargetEvicted, lifecyclev1alpha1.EvictionConditionReasonPodDeleted)),
|
|
mkValidEviction("pod-7-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionTargetEvicted, lifecyclev1alpha1.EvictionConditionReasonPodDeleted)),
|
|
mkValidEviction("pod-8-foo", "foo", "uid-1"),
|
|
},
|
|
expectedEviction: mkValidEviction("pod-7-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionTargetEvicted, lifecyclev1alpha1.EvictionConditionReasonPodDeleted)),
|
|
},
|
|
{
|
|
name: "oldest_active_eviction",
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-foo", "foo", "uid-1", setCreationTimestamp(clockOlder)),
|
|
mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
mkValidEviction("pod-3-foo", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
},
|
|
expectedEviction: mkValidEviction("pod-3-foo", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
},
|
|
{
|
|
name: "multiple_oldest_active_eviction",
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-foo", "foo", "uid-1", setCreationTimestamp(clockOlder)),
|
|
mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
mkValidEviction("abc", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
mkValidEviction("pod-4-foo", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
mkValidEviction("pod-3-foo", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
},
|
|
expectedEviction: mkValidEviction("pod-3-foo", "foo", "uid-1", setCreationTimestamp(clockOldest)),
|
|
},
|
|
{
|
|
name: "oldest_active_eviction_with_conditions",
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-foo", "foo", "uid-1", setCreationTimestamp(clockOlder)),
|
|
mkValidEviction("pod-2-foo", "foo", "uid-1", setCreationTimestamp(clockOldest),
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
mkValidEviction("pod-3-foo", "foo", "uid-1", setCreationTimestamp(clockOldest),
|
|
addCondition(clockOldest, lifecyclev1alpha1.EvictionConditionTargetEvicted, metav1.ConditionFalse, lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction),
|
|
addCondition(clockOldest, lifecyclev1alpha1.EvictionConditionFailed, metav1.ConditionFalse, lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction)),
|
|
mkValidEviction("pod-4-foo", "foo", "uid-1", setCreationTimestamp(clockOlder)),
|
|
},
|
|
expectedEviction: mkValidEviction("pod-3-foo", "foo", "uid-1", setCreationTimestamp(clockOldest),
|
|
addCondition(clockOldest, lifecyclev1alpha1.EvictionConditionTargetEvicted, metav1.ConditionFalse, lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction),
|
|
addCondition(clockOldest, lifecyclev1alpha1.EvictionConditionFailed, metav1.ConditionFalse, lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction)),
|
|
},
|
|
{
|
|
name: "latest_failed_eviction",
|
|
evictions: []*lifecyclev1alpha1.Eviction{
|
|
mkValidEviction("pod-1-foo", "foo", "uid-1",
|
|
addConditionTrue(clockOldest, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
},
|
|
expectedEviction: mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder)),
|
|
expectedShouldCreate: true,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
rand.Shuffle(len(tc.evictions), func(i, j int) {
|
|
tc.evictions[i], tc.evictions[j] = tc.evictions[j], tc.evictions[i]
|
|
})
|
|
// also tests compareConditionTransitionTime
|
|
relevantEviction, shouldCreate := findRelevantEviction(tc.evictions)
|
|
if diff := cmp.Diff(tc.expectedEviction, relevantEviction); len(diff) > 0 {
|
|
t.Fatalf("unexpected relevant eviction (-want +got):\n%s", diff)
|
|
}
|
|
if tc.expectedShouldCreate != shouldCreate {
|
|
t.Errorf("got shouldCreate %v, expected %v", shouldCreate, tc.expectedShouldCreate)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvertToEvictionRequestConditions(t *testing.T) {
|
|
now := time.Now()
|
|
clock := testing2.NewFakeClock(now)
|
|
clockOld := testing2.NewFakeClock(now.Add(-time.Minute))
|
|
testCases := []struct {
|
|
name string
|
|
existingConditions []metav1.Condition
|
|
evictionConditions []metav1.Condition
|
|
conditionType lifecyclev1alpha1.EvictionConditionType
|
|
expectedApplyConfiguration *metav1ac.ConditionApplyConfiguration
|
|
expectChanged bool
|
|
}{
|
|
{
|
|
name: "find_and_set_correct_condition",
|
|
existingConditions: nil,
|
|
evictionConditions: []metav1.Condition{
|
|
{Type: "ResponderCondition", Status: metav1.ConditionTrue,
|
|
Reason: "foo", Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionTrue,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted), Message: "msg"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2"},
|
|
},
|
|
conditionType: lifecyclev1alpha1.EvictionConditionTargetEvicted,
|
|
expectedApplyConfiguration: metav1ac.Condition().
|
|
WithType(string(lifecyclev1alpha1.EvictionConditionTargetEvicted)).
|
|
WithStatus(metav1.ConditionTrue).
|
|
WithReason(string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted)).
|
|
WithMessage("msg").
|
|
WithLastTransitionTime(metav1.Time{Time: clock.Now()}),
|
|
expectChanged: true,
|
|
},
|
|
{
|
|
name: "find_and_update_correct_condition",
|
|
existingConditions: []metav1.Condition{
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction), Message: "awaiting", LastTransitionTime: metav1.Time{Time: clockOld.Now()}},
|
|
},
|
|
evictionConditions: []metav1.Condition{
|
|
{Type: "ResponderCondition", Status: metav1.ConditionTrue,
|
|
Reason: "foo", Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionTrue,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted), Message: "msg", LastTransitionTime: metav1.Time{Time: clock.Now()}},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2", LastTransitionTime: metav1.Time{Time: clock.Now()}},
|
|
},
|
|
conditionType: lifecyclev1alpha1.EvictionConditionTargetEvicted,
|
|
expectedApplyConfiguration: metav1ac.Condition().
|
|
WithType(string(lifecyclev1alpha1.EvictionConditionTargetEvicted)).
|
|
WithStatus(metav1.ConditionTrue).
|
|
WithReason(string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted)).
|
|
WithMessage("msg").
|
|
WithLastTransitionTime(metav1.Time{Time: clock.Now()}),
|
|
expectChanged: true,
|
|
},
|
|
{
|
|
name: "find_and_update_correct_condition_without_transition",
|
|
existingConditions: []metav1.Condition{
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionTrue,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted), Message: "ms3", LastTransitionTime: metav1.Time{Time: clockOld.Now()}},
|
|
},
|
|
evictionConditions: []metav1.Condition{
|
|
{Type: "ResponderCondition", Status: metav1.ConditionTrue,
|
|
Reason: "foo", Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionTrue,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted), Message: "msg"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2"},
|
|
},
|
|
conditionType: lifecyclev1alpha1.EvictionConditionTargetEvicted,
|
|
expectedApplyConfiguration: metav1ac.Condition().
|
|
WithType(string(lifecyclev1alpha1.EvictionConditionTargetEvicted)).
|
|
WithStatus(metav1.ConditionTrue).
|
|
WithReason(string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted)).
|
|
WithMessage("msg").
|
|
WithLastTransitionTime(metav1.Time{Time: clockOld.Now()}),
|
|
expectChanged: true,
|
|
},
|
|
{
|
|
name: "cannot_find_the_correct_condition",
|
|
existingConditions: nil,
|
|
evictionConditions: []metav1.Condition{
|
|
{Type: "ResponderCondition", Status: metav1.ConditionTrue,
|
|
Reason: "foo", Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2"},
|
|
},
|
|
conditionType: lifecyclev1alpha1.EvictionConditionTargetEvicted,
|
|
expectedApplyConfiguration: metav1ac.Condition().
|
|
WithType(string(lifecyclev1alpha1.EvictionConditionTargetEvicted)).
|
|
WithStatus(metav1.ConditionFalse).
|
|
WithReason(string(lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction)).
|
|
WithMessage("waiting for an Eviction to report on progress").
|
|
WithLastTransitionTime(metav1.Time{Time: clock.Now()}),
|
|
expectChanged: true,
|
|
},
|
|
|
|
{
|
|
name: "cannot_find_the_correct_condition(no_eviction)",
|
|
existingConditions: nil,
|
|
evictionConditions: nil,
|
|
conditionType: lifecyclev1alpha1.EvictionConditionTargetEvicted,
|
|
expectedApplyConfiguration: metav1ac.Condition().
|
|
WithType(string(lifecyclev1alpha1.EvictionConditionTargetEvicted)).
|
|
WithStatus(metav1.ConditionFalse).
|
|
WithReason(string(lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction)).
|
|
WithMessage("waiting for an Eviction to report on progress").
|
|
WithLastTransitionTime(metav1.Time{Time: clock.Now()}),
|
|
expectChanged: true,
|
|
},
|
|
{
|
|
name: "find_correct_condition_with_no_change",
|
|
existingConditions: []metav1.Condition{
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionTrue,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted), Message: "msg", LastTransitionTime: metav1.Time{Time: clockOld.Now()}},
|
|
},
|
|
evictionConditions: []metav1.Condition{
|
|
{Type: "ResponderCondition", Status: metav1.ConditionTrue,
|
|
Reason: "foo", Message: "msg2"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionTargetEvicted), Status: metav1.ConditionTrue,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted), Message: "msg"},
|
|
{Type: string(lifecyclev1alpha1.EvictionConditionFailed), Status: metav1.ConditionFalse,
|
|
Reason: string(lifecyclev1alpha1.EvictionConditionReasonSucceeded), Message: "msg2"},
|
|
},
|
|
conditionType: lifecyclev1alpha1.EvictionConditionTargetEvicted,
|
|
expectedApplyConfiguration: metav1ac.Condition().
|
|
WithType(string(lifecyclev1alpha1.EvictionConditionTargetEvicted)).
|
|
WithStatus(metav1.ConditionTrue).
|
|
WithReason(string(lifecyclev1alpha1.EvictionConditionReasonPodDeleted)).
|
|
WithMessage("msg").
|
|
WithLastTransitionTime(metav1.Time{Time: clockOld.Now()}),
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
evictionRequest := &lifecyclev1alpha1.EvictionRequest{}
|
|
evictionRequest.Status.Conditions = tc.existingConditions
|
|
var eviction *lifecyclev1alpha1.Eviction
|
|
if len(tc.evictionConditions) > 0 {
|
|
eviction = &lifecyclev1alpha1.Eviction{}
|
|
eviction.Status.Conditions = tc.evictionConditions
|
|
}
|
|
// also tests setCondition
|
|
got, changed := convertToEvictionRequestConditions(clock.Now(), evictionRequest, eviction, tc.conditionType)
|
|
if diff := cmp.Diff(tc.expectedApplyConfiguration, got); len(diff) > 0 {
|
|
t.Fatalf("unexpected ApplyConfiguration (-want +got):\n%s", diff)
|
|
}
|
|
if changed != tc.expectChanged {
|
|
t.Errorf("got changed %v, want %v", changed, tc.expectChanged)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompareConditionTransitionTime(t *testing.T) {
|
|
now := time.Now()
|
|
clock := testing2.NewFakeClock(now)
|
|
clockLater := testing2.NewFakeClock(now.Add(time.Hour))
|
|
tests := []struct {
|
|
name string
|
|
evictionA *lifecyclev1alpha1.Eviction
|
|
evictionB *lifecyclev1alpha1.Eviction
|
|
expectedCMP int
|
|
}{
|
|
{
|
|
name: "no_conditions",
|
|
evictionA: mkValidEviction("pod-1-foo", "foo", "uid-1"),
|
|
evictionB: mkValidEviction("pod-2-foo", "foo", "uid-1"),
|
|
expectedCMP: 0,
|
|
},
|
|
{
|
|
name: "same_transition_time",
|
|
evictionA: mkValidEviction("pod-1-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
evictionB: mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
expectedCMP: 0,
|
|
},
|
|
{
|
|
name: "no_a_condition",
|
|
evictionA: mkValidEviction("pod-1-foo", "foo", "uid-1"),
|
|
evictionB: mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
expectedCMP: -1,
|
|
},
|
|
{
|
|
name: "no_b_condition",
|
|
evictionA: mkValidEviction("pod-1-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
evictionB: mkValidEviction("pod-2-foo", "foo", "uid-1"),
|
|
expectedCMP: 1,
|
|
},
|
|
|
|
{
|
|
name: "a_is_newest",
|
|
evictionA: mkValidEviction("pod-1-foo", "foo", "uid-1",
|
|
addConditionTrue(clockLater, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
evictionB: mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
expectedCMP: 1,
|
|
},
|
|
{
|
|
name: "b_is_newest",
|
|
evictionA: mkValidEviction("pod-1-foo", "foo", "uid-1",
|
|
addConditionTrue(clock, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
evictionB: mkValidEviction("pod-2-foo", "foo", "uid-1",
|
|
addConditionTrue(clockLater, lifecyclev1alpha1.EvictionConditionFailed, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
expectedCMP: -1,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if cmp := compareConditionTransitionTime(tc.evictionA, tc.evictionB, lifecyclev1alpha1.EvictionConditionFailed); tc.expectedCMP != cmp {
|
|
t.Errorf("got cmp %v, expected %v", cmp, tc.expectedCMP)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasEvictionRequestCompleted(t *testing.T) {
|
|
now := time.Now()
|
|
clock := testing2.NewFakeClock(now)
|
|
tests := []struct {
|
|
name string
|
|
evictionRequest *lifecyclev1alpha1.EvictionRequest
|
|
expecteCompleted bool
|
|
}{
|
|
{
|
|
name: "completed(target_evicted)",
|
|
evictionRequest: mkValidEvictionRequest("foo", "uid-1",
|
|
addERCondition(clock, lifecyclev1alpha1.EvictionConditionTargetEvicted, metav1.ConditionTrue, lifecyclev1alpha1.EvictionConditionReasonPodDeleted),
|
|
),
|
|
expecteCompleted: true,
|
|
},
|
|
{
|
|
name: "completed(invalid)",
|
|
evictionRequest: mkValidEvictionRequest("foo", "uid-1",
|
|
addERCondition(clock, lifecyclev1alpha1.EvictionConditionFailed, metav1.ConditionTrue, lifecyclev1alpha1.EvictionConditionReasonEvictionInvalid),
|
|
),
|
|
expecteCompleted: true,
|
|
},
|
|
{
|
|
name: "in_progress(with_last_failed_eviction)",
|
|
evictionRequest: mkValidEvictionRequest("foo", "uid-1",
|
|
addERCondition(clock, lifecyclev1alpha1.EvictionConditionFailed, metav1.ConditionTrue, lifecyclev1alpha1.EvictionConditionReasonNoFurtherResponder),
|
|
),
|
|
expecteCompleted: false,
|
|
},
|
|
{
|
|
name: "in_progress",
|
|
evictionRequest: mkValidEvictionRequest("foo", "uid-1",
|
|
addERCondition(clock, lifecyclev1alpha1.EvictionConditionTargetEvicted, metav1.ConditionFalse, lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction),
|
|
addERCondition(clock, lifecyclev1alpha1.EvictionConditionFailed, metav1.ConditionFalse, lifecyclev1alpha1.EvictionConditionReasonAwaitingEviction),
|
|
),
|
|
expecteCompleted: false,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := hasEvictionRequestCompleted(tc.evictionRequest); tc.expecteCompleted != got {
|
|
t.Errorf("got eviction request completed %v, expected %v", got, tc.expecteCompleted)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasEvictionRequestEvictionIntent(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
eviction []*lifecyclev1alpha1.EvictionRequest
|
|
expectedEvictionIntent bool
|
|
}{
|
|
{
|
|
name: "multiple_intents",
|
|
eviction: []*lifecyclev1alpha1.EvictionRequest{
|
|
mkValidEvictionRequest("foo1", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentEviction)),
|
|
mkValidEvictionRequest("foo2", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentEviction)),
|
|
mkValidEvictionRequest("foo3", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentEviction)),
|
|
},
|
|
expectedEvictionIntent: true,
|
|
},
|
|
{
|
|
name: "single_intent",
|
|
eviction: []*lifecyclev1alpha1.EvictionRequest{
|
|
mkValidEvictionRequest("foo1", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo2", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo3", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo4", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentEviction)),
|
|
mkValidEvictionRequest("foo5", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo6", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
},
|
|
expectedEvictionIntent: true,
|
|
},
|
|
{
|
|
name: "withdrawn",
|
|
eviction: []*lifecyclev1alpha1.EvictionRequest{
|
|
mkValidEvictionRequest("foo1", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo2", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo3", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo4", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentEviction),
|
|
setERDeletionTimestamp(new(metav1.Now()))),
|
|
mkValidEvictionRequest("foo5", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
mkValidEvictionRequest("foo6", "uid-1", setERIntent(lifecyclev1alpha1.EvictionRequestIntentWithdrawn)),
|
|
},
|
|
expectedEvictionIntent: false,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := hasEvictionIntent(tc.eviction); tc.expectedEvictionIntent != got {
|
|
t.Errorf("got eviction request completed %v, expected %v", got, tc.expectedEvictionIntent)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvictionRequestAsOwnerReference(t *testing.T) {
|
|
evictionRequest := mkValidEvictionRequest("foo1", "uid-1")
|
|
expectedOwnerRef := metav1ac.OwnerReference().
|
|
WithKind("EvictionRequest").
|
|
WithAPIVersion("lifecycle.k8s.io/v1alpha1").
|
|
WithName(evictionRequest.Name).
|
|
WithUID(evictionRequest.UID)
|
|
gotOwnerRef := evictionRequestAsOwnerReference(evictionRequest)
|
|
if diff := cmp.Diff(expectedOwnerRef, gotOwnerRef); len(diff) > 0 {
|
|
t.Errorf(" unexpected eviction request ownerRef (-want +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
func TestComputeEvictionLabels(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
eviction *lifecyclev1alpha1.Eviction
|
|
target targetInfo
|
|
targetEvictionRequest []*lifecyclev1alpha1.EvictionRequest
|
|
expectedLabels map[string]string
|
|
}{
|
|
{
|
|
name: "computes_labels",
|
|
eviction: mkValidEviction("pod-1-foo", "foo", "uid-1", setLabels(map[string]string{
|
|
"foo": "bar", // not used - computeEvictionLabels computes just the SSA update
|
|
})),
|
|
target: newTargetInfoForEviction(mkValidPodTarget("my-pod", "uid-1"),
|
|
mkValidPod("my-pod", "uid-1", setEvictionResponders(
|
|
v1.EvictionResponder{Name: "responder-a.example.com/a", Priority: new(int32(1000))},
|
|
v1.EvictionResponder{Name: "responder-b.example.com/b", Priority: new(int32(15000))},
|
|
)),
|
|
),
|
|
targetEvictionRequest: []*lifecyclev1alpha1.EvictionRequest{
|
|
mkValidEvictionRequest("requester-1", "pod-1", setERRequester("foo.example.com/1")),
|
|
mkValidEvictionRequest("requester-2", "pod-2", setERRequester("responder-a.example.com/a")),
|
|
},
|
|
expectedLabels: map[string]string{
|
|
"foo.example.com/1": string(lifecyclev1alpha1.EvictionParticipantRoleRequester),
|
|
"responder-a.example.com/a": string(lifecyclev1alpha1.EvictionParticipantRoleRequesterResponder),
|
|
"responder-b.example.com/b": string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
lifecyclev1alpha1.EvictionResponderImperativeEviction: string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
},
|
|
},
|
|
{
|
|
name: "target_gone_persists_labels",
|
|
eviction: mkValidEviction("pod-1-foo", "foo", "uid-1", setLabels(map[string]string{
|
|
"foo": "bar", // not used - computeEvictionLabels computes just the SSA update
|
|
"foo.example.com/1": string(lifecyclev1alpha1.EvictionParticipantRoleRequester),
|
|
"responder-a.example.com/a": string(lifecyclev1alpha1.EvictionParticipantRoleRequesterResponder),
|
|
"responder-b.example.com/b": string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
lifecyclev1alpha1.EvictionResponderImperativeEviction: string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
})),
|
|
target: newTargetInfoForEviction(mkValidPodTarget("my-pod", "uid-1"), nil),
|
|
targetEvictionRequest: []*lifecyclev1alpha1.EvictionRequest{
|
|
mkValidEvictionRequest("requester-1", "pod-1", setERRequester("foo.example.com/1")),
|
|
mkValidEvictionRequest("requester-2", "pod-2", setERRequester("responder-a.example.com/a")),
|
|
},
|
|
expectedLabels: map[string]string{
|
|
"foo.example.com/1": string(lifecyclev1alpha1.EvictionParticipantRoleRequester),
|
|
"responder-a.example.com/a": string(lifecyclev1alpha1.EvictionParticipantRoleRequesterResponder),
|
|
"responder-b.example.com/b": string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
lifecyclev1alpha1.EvictionResponderImperativeEviction: string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "target_gone_persists_labels_but_requesters_rotated",
|
|
eviction: mkValidEviction("pod-1-foo", "foo", "uid-1", setLabels(map[string]string{
|
|
"foo": "bar", // not used - computeEvictionLabels computes just the SSA update
|
|
"foo.example.com/1": string(lifecyclev1alpha1.EvictionParticipantRoleRequester),
|
|
"responder-a.example.com/a": string(lifecyclev1alpha1.EvictionParticipantRoleRequesterResponder),
|
|
"responder-b.example.com/b": string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
lifecyclev1alpha1.EvictionResponderImperativeEviction: string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
})),
|
|
target: newTargetInfoForEviction(mkValidPodTarget("my-pod", "uid-1"), nil),
|
|
targetEvictionRequest: []*lifecyclev1alpha1.EvictionRequest{
|
|
mkValidEvictionRequest("requester-3", "pod-3", setERRequester("bar.example.com/1")),
|
|
mkValidEvictionRequest("requester-4", "pod-4", setERRequester("responder-b.example.com/b")),
|
|
},
|
|
expectedLabels: map[string]string{
|
|
"bar.example.com/1": string(lifecyclev1alpha1.EvictionParticipantRoleRequester),
|
|
"responder-a.example.com/a": string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
"responder-b.example.com/b": string(lifecyclev1alpha1.EvictionParticipantRoleRequesterResponder),
|
|
lifecyclev1alpha1.EvictionResponderImperativeEviction: string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := computeEvictionLabels(tc.eviction, tc.target, tc.targetEvictionRequest)
|
|
if diff := cmp.Diff(tc.expectedLabels, got); len(diff) > 0 {
|
|
t.Errorf("unexpected eviction labels (-want +got):\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvictionLabelsNeedSSAUpdate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
oldLabels map[string]string
|
|
newLabels map[string]string
|
|
needsUpdate bool
|
|
}{
|
|
{
|
|
name: "new_label",
|
|
oldLabels: nil,
|
|
newLabels: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
needsUpdate: true,
|
|
},
|
|
{
|
|
name: "label_update",
|
|
oldLabels: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
newLabels: map[string]string{
|
|
"foo": "baz",
|
|
},
|
|
needsUpdate: true,
|
|
},
|
|
|
|
{
|
|
name: "removed_requester_update",
|
|
oldLabels: map[string]string{
|
|
"baz": "bar",
|
|
"foo.example.com/bar": string(lifecyclev1alpha1.EvictionParticipantRoleRequester),
|
|
},
|
|
newLabels: map[string]string{
|
|
"baz": "bar",
|
|
},
|
|
needsUpdate: true,
|
|
},
|
|
{
|
|
name: "removal_of_other_values_not_registered_requester_update",
|
|
oldLabels: map[string]string{
|
|
"baz": "bar",
|
|
"foo.example.com/bar": string(lifecyclev1alpha1.EvictionParticipantRoleResponder),
|
|
},
|
|
newLabels: nil,
|
|
needsUpdate: false,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := evictionLabelsNeedSSAUpdate(tc.oldLabels, tc.newLabels); tc.needsUpdate != got {
|
|
t.Errorf("got eviction request needsUpdate %v, expected %v", got, tc.needsUpdate)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestShouldDeferCompletion(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
tests := []struct {
|
|
name string
|
|
activeResponderStatus *lifecyclev1alpha1.ResponderStatus
|
|
terminalTime *time.Time
|
|
expectDefer *time.Duration
|
|
}{
|
|
{
|
|
name: "no_responder_status",
|
|
terminalTime: new(now.Add(-time.Second)),
|
|
expectDefer: nil,
|
|
},
|
|
{
|
|
name: "responder_with_completion_time",
|
|
activeResponderStatus: &lifecyclev1alpha1.ResponderStatus{CompletionTime: &metav1.Time{Time: now}},
|
|
terminalTime: new(now.Add(-time.Second)),
|
|
expectDefer: nil,
|
|
},
|
|
{
|
|
name: "no_pod_or_terminal_time(deletion_timestamp)",
|
|
activeResponderStatus: &lifecyclev1alpha1.ResponderStatus{},
|
|
terminalTime: nil,
|
|
expectDefer: nil,
|
|
},
|
|
{
|
|
name: "planned_deletion_should_defer",
|
|
activeResponderStatus: &lifecyclev1alpha1.ResponderStatus{},
|
|
terminalTime: new(now.Add(time.Minute)),
|
|
expectDefer: new(time.Minute + 5*time.Second),
|
|
},
|
|
{
|
|
name: "recent_deletion_should_defer",
|
|
activeResponderStatus: &lifecyclev1alpha1.ResponderStatus{},
|
|
terminalTime: new(now.Add(-time.Second)),
|
|
expectDefer: new(4 * time.Second),
|
|
},
|
|
{
|
|
name: "old_deletion_should_not_defer",
|
|
activeResponderStatus: &lifecyclev1alpha1.ResponderStatus{},
|
|
terminalTime: new(now.Add(-5 * time.Second)),
|
|
expectDefer: nil,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := shouldDeferCompletion(now, tc.activeResponderStatus, tc.terminalTime); !ptr.Equal(got, tc.expectDefer) {
|
|
t.Errorf("got defer completion %v, expected %v", ptr.Deref(got, -1), ptr.Deref(tc.expectDefer, -1))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSortTargetResponders(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input []lifecyclev1alpha1.TargetResponder
|
|
expected []lifecyclev1alpha1.TargetResponder
|
|
}{
|
|
{
|
|
name: "empty",
|
|
input: nil,
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "sort",
|
|
input: []lifecyclev1alpha1.TargetResponder{
|
|
{Name: "bar", Priority: new(int32(3))},
|
|
{Name: "foo", Priority: new(int32(5))},
|
|
{Name: "baz", Priority: new(int32(4))},
|
|
},
|
|
expected: []lifecyclev1alpha1.TargetResponder{
|
|
{Name: "foo", Priority: new(int32(5))},
|
|
{Name: "baz", Priority: new(int32(4))},
|
|
{Name: "bar", Priority: new(int32(3))},
|
|
},
|
|
},
|
|
// Advanced tests are in valdidation.SortTargetResponders
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := sortTargetResponders(tc.input)
|
|
if diff := cmp.Diff(tc.expected, result); len(diff) > 0 {
|
|
t.Errorf("unexpected sortTargetResponders (-want +got):\n%s", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func mkValidEviction(name, podName, podUID string, tweaks ...func(obj *lifecyclev1alpha1.Eviction)) *lifecyclev1alpha1.Eviction {
|
|
obj := &lifecyclev1alpha1.Eviction{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
},
|
|
Spec: lifecyclev1alpha1.EvictionSpec{
|
|
Target: lifecyclev1alpha1.EvictionTarget{
|
|
Pod: &lifecyclev1alpha1.EvictionPodReference{
|
|
UID: apimachinerytypes.UID(podUID),
|
|
Name: podName,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tweak := range tweaks {
|
|
tweak(obj)
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
func setCreationTimestamp(passiveClock clock.PassiveClock) func(obj *lifecyclev1alpha1.Eviction) {
|
|
return func(obj *lifecyclev1alpha1.Eviction) {
|
|
obj.CreationTimestamp = metav1.Time{Time: passiveClock.Now()}
|
|
}
|
|
}
|
|
func addConditionTrue(passiveClock clock.PassiveClock, conditionName lifecyclev1alpha1.EvictionConditionType, conditionReason lifecyclev1alpha1.EvictionConditionReason) func(obj *lifecyclev1alpha1.Eviction) {
|
|
return addCondition(passiveClock, conditionName, metav1.ConditionTrue, conditionReason)
|
|
}
|
|
func addCondition(passiveClock clock.PassiveClock, conditionName lifecyclev1alpha1.EvictionConditionType, conditionStatus metav1.ConditionStatus, conditionReason lifecyclev1alpha1.EvictionConditionReason) func(obj *lifecyclev1alpha1.Eviction) {
|
|
return func(obj *lifecyclev1alpha1.Eviction) {
|
|
obj.Status.Conditions = append(obj.Status.Conditions, metav1.Condition{
|
|
Type: string(conditionName),
|
|
Status: conditionStatus,
|
|
Reason: string(conditionReason),
|
|
LastTransitionTime: metav1.Time{Time: passiveClock.Now()},
|
|
Message: "msg",
|
|
})
|
|
}
|
|
}
|
|
func addTargetResponders(responders ...string) func(obj *lifecyclev1alpha1.Eviction) {
|
|
return func(obj *lifecyclev1alpha1.Eviction) {
|
|
lastPriority := len(obj.Status.TargetResponders)
|
|
for i, name := range responders {
|
|
obj.Status.TargetResponders = append(obj.Status.TargetResponders, lifecyclev1alpha1.TargetResponder{
|
|
Name: name,
|
|
Priority: new(5000 - int32(lastPriority+i)),
|
|
State: lifecyclev1alpha1.ResponderStateInactive,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
func setLabels(labels map[string]string) func(obj *lifecyclev1alpha1.Eviction) {
|
|
return func(obj *lifecyclev1alpha1.Eviction) {
|
|
obj.Labels = labels
|
|
}
|
|
}
|
|
|
|
func setStateFor(state lifecyclev1alpha1.ResponderStateType, idx int) func(obj *lifecyclev1alpha1.Eviction) {
|
|
return func(obj *lifecyclev1alpha1.Eviction) {
|
|
obj.Status.TargetResponders[idx].State = state
|
|
}
|
|
}
|
|
|
|
func mkValidPod(name, uid string, tweaks ...func(obj *v1.Pod)) *v1.Pod {
|
|
obj := &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
UID: apimachinerytypes.UID(uid),
|
|
},
|
|
}
|
|
for _, tweak := range tweaks {
|
|
tweak(obj)
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
func setPodDeletionTimestamp(intent *metav1.Time) func(obj *v1.Pod) {
|
|
return func(obj *v1.Pod) {
|
|
obj.DeletionTimestamp = intent
|
|
}
|
|
}
|
|
func setPodDeletionPhase(phase v1.PodPhase) func(obj *v1.Pod) {
|
|
return func(obj *v1.Pod) {
|
|
obj.Status.Phase = phase
|
|
}
|
|
}
|
|
func setPodContainerStatuses(statuses ...v1.ContainerStatus) func(obj *v1.Pod) {
|
|
return func(obj *v1.Pod) {
|
|
obj.Status.ContainerStatuses = statuses
|
|
}
|
|
}
|
|
func setEvictionResponders(evictionResponders ...v1.EvictionResponder) func(obj *v1.Pod) {
|
|
return func(obj *v1.Pod) {
|
|
obj.Spec.EvictionResponders = evictionResponders
|
|
|
|
}
|
|
}
|
|
|
|
func mkValidPodTarget(name, uid string) lifecyclev1alpha1.EvictionTarget {
|
|
obj := lifecyclev1alpha1.EvictionTarget{
|
|
Pod: &lifecyclev1alpha1.EvictionPodReference{
|
|
Name: name,
|
|
UID: apimachinerytypes.UID(uid),
|
|
},
|
|
}
|
|
return obj
|
|
}
|
|
func mkValidEvictionRequestPodTarget(name, uid string) lifecyclev1alpha1.EvictionRequestTarget {
|
|
obj := lifecyclev1alpha1.EvictionRequestTarget{
|
|
Pod: &lifecyclev1alpha1.EvictionRequestPodReference{
|
|
Name: name,
|
|
UID: apimachinerytypes.UID(uid),
|
|
},
|
|
}
|
|
return obj
|
|
}
|
|
|
|
func mkValidEvictionRequest(podName, podUID string, tweaks ...func(obj *lifecyclev1alpha1.EvictionRequest)) *lifecyclev1alpha1.EvictionRequest {
|
|
obj := &lifecyclev1alpha1.EvictionRequest{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "foo",
|
|
CreationTimestamp: metav1.Now(),
|
|
},
|
|
Spec: lifecyclev1alpha1.EvictionRequestSpec{
|
|
Target: lifecyclev1alpha1.EvictionRequestTarget{
|
|
Pod: &lifecyclev1alpha1.EvictionRequestPodReference{
|
|
UID: apimachinerytypes.UID(podUID),
|
|
Name: podName,
|
|
},
|
|
},
|
|
Requester: "foo.example.com/bar",
|
|
Intent: lifecyclev1alpha1.EvictionRequestIntentEviction,
|
|
},
|
|
}
|
|
for _, tweak := range tweaks {
|
|
tweak(obj)
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
func setERIntent(intent lifecyclev1alpha1.EvictionRequestIntent) func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
return func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
obj.Spec.Intent = intent
|
|
}
|
|
}
|
|
func setERRequester(name string) func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
return func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
obj.Spec.Requester = name
|
|
}
|
|
}
|
|
func setERCreationTimestamp(time metav1.Time) func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
return func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
obj.CreationTimestamp = time
|
|
}
|
|
}
|
|
func setERDeletionTimestamp(intent *metav1.Time) func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
return func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
obj.DeletionTimestamp = intent
|
|
}
|
|
}
|
|
func addERCondition(passiveClock clock.PassiveClock, conditionName lifecyclev1alpha1.EvictionConditionType, conditionStatus metav1.ConditionStatus, conditionReason lifecyclev1alpha1.EvictionConditionReason) func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
return func(obj *lifecyclev1alpha1.EvictionRequest) {
|
|
obj.Status.Conditions = append(obj.Status.Conditions, metav1.Condition{
|
|
Type: string(conditionName),
|
|
Status: conditionStatus,
|
|
Reason: string(conditionReason),
|
|
LastTransitionTime: metav1.Time{Time: passiveClock.Now()},
|
|
Message: "msg",
|
|
})
|
|
}
|
|
}
|