Files
kubernetes/pkg/controller/evictionrequest/evictionrequest_utils_test.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",
})
}
}