Skip to content

Commit f3c58da

Browse files
authored
Merge pull request #8273 from krzysied/fake-pods
Omit fake pods during eviction
2 parents 14ce611 + 8217888 commit f3c58da

File tree

10 files changed

+130
-33
lines changed

10 files changed

+130
-33
lines changed

cluster-autoscaler/core/scaledown/actuation/drain.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
kube_errors "k8s.io/apimachinery/pkg/api/errors"
2828
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929
"k8s.io/autoscaler/cluster-autoscaler/metrics"
30+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
3031
"k8s.io/autoscaler/cluster-autoscaler/simulator/framework"
3132
"k8s.io/klog/v2"
3233
kubelet_config "k8s.io/kubernetes/pkg/kubelet/apis/config"
@@ -276,6 +277,8 @@ func podsToEvict(nodeInfo *framework.NodeInfo, evictDsByDefault bool) (dsPods, n
276277
for _, podInfo := range nodeInfo.Pods() {
277278
if pod_util.IsMirrorPod(podInfo.Pod) {
278279
continue
280+
} else if fake.IsFake(podInfo.Pod) {
281+
continue
279282
} else if pod_util.IsDaemonSetPod(podInfo.Pod) {
280283
dsPods = append(dsPods, podInfo.Pod)
281284
} else {

cluster-autoscaler/core/scaledown/actuation/drain_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"k8s.io/autoscaler/cluster-autoscaler/core/utils"
4141
"k8s.io/autoscaler/cluster-autoscaler/simulator/clustersnapshot"
4242
"k8s.io/autoscaler/cluster-autoscaler/simulator/clustersnapshot/testsnapshot"
43+
simulator_fake "k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
4344
"k8s.io/autoscaler/cluster-autoscaler/simulator/framework"
4445
"k8s.io/autoscaler/cluster-autoscaler/utils/daemonset"
4546
kube_util "k8s.io/autoscaler/cluster-autoscaler/utils/kubernetes"
@@ -683,6 +684,11 @@ func TestPodsToEvict(t *testing.T) {
683684
wantDsPods: []*apiv1.Pod{},
684685
wantNonDsPods: []*apiv1.Pod{},
685686
},
687+
"fake pods are never returned": {
688+
pods: []*apiv1.Pod{fakePod("pod-1"), fakePod("pod-2")},
689+
wantDsPods: []*apiv1.Pod{},
690+
wantNonDsPods: []*apiv1.Pod{},
691+
},
686692
"non-DS pods are correctly returned": {
687693
pods: []*apiv1.Pod{regularPod("pod-1"), regularPod("pod-2")},
688694
wantDsPods: []*apiv1.Pod{},
@@ -766,6 +772,10 @@ func mirrorPod(name string) *apiv1.Pod {
766772
}
767773
}
768774

775+
func fakePod(name string) *apiv1.Pod {
776+
return simulator_fake.WithFakePodAnnotation(regularPod(name))
777+
}
778+
769779
func dsPod(name string, evictable bool) *apiv1.Pod {
770780
pod := &apiv1.Pod{
771781
ObjectMeta: metav1.ObjectMeta{

cluster-autoscaler/processors/podinjection/enforce_injected_pods_limit_processor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
apiv1 "k8s.io/api/core/v1"
2121
"k8s.io/autoscaler/cluster-autoscaler/context"
2222
"k8s.io/autoscaler/cluster-autoscaler/metrics"
23+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
2324
)
2425

2526
const (
@@ -50,7 +51,7 @@ func (p *EnforceInjectedPodsLimitProcessor) Process(ctx *context.AutoscalingCont
5051
var unschedulablePodsAfterProcessing []*apiv1.Pod
5152

5253
for _, pod := range unschedulablePods {
53-
if IsFake(pod) {
54+
if fake.IsFake(pod) {
5455
if removedFakePodsCount < numberOfFakePodsToRemove {
5556
removedFakePodsCount += 1
5657
continue

cluster-autoscaler/processors/podinjection/enforce_injected_pods_limit_processor_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/stretchr/testify/assert"
2323
apiv1 "k8s.io/api/core/v1"
2424
"k8s.io/apimachinery/pkg/types"
25+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
2526
)
2627

2728
func TestEnforceInjectedPodsLimitProcessor(t *testing.T) {
@@ -110,7 +111,7 @@ func TestEnforceInjectedPodsLimitProcessor(t *testing.T) {
110111
func numberOfFakePods(pods []*apiv1.Pod) int {
111112
numberOfFakePods := 0
112113
for _, pod := range pods {
113-
if IsFake(pod) {
114+
if fake.IsFake(pod) {
114115
numberOfFakePods += 1
115116
}
116117
}

cluster-autoscaler/processors/podinjection/pod_injection_processor.go

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,11 @@ import (
2424
"k8s.io/apimachinery/pkg/types"
2525
"k8s.io/autoscaler/cluster-autoscaler/context"
2626
podinjectionbackoff "k8s.io/autoscaler/cluster-autoscaler/processors/podinjection/backoff"
27+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
2728
"k8s.io/autoscaler/cluster-autoscaler/simulator/framework"
2829
"k8s.io/klog/v2"
2930
)
3031

31-
const (
32-
// FakePodAnnotationKey the key for pod type
33-
FakePodAnnotationKey = "podtype"
34-
// FakePodAnnotationValue the value for a fake pod
35-
FakePodAnnotationValue = "fakepod"
36-
)
37-
3832
// PodInjectionPodListProcessor is a PodListProcessor used to inject fake pods to consider replica count in the respective controllers for the scale-up.
3933
// For each controller, #fake pods injected = #replicas specified the controller - #scheduled pods - #finished pods - #unschedulable pods
4034
type PodInjectionPodListProcessor struct {
@@ -90,7 +84,7 @@ func (p *PodInjectionPodListProcessor) CleanUp() {
9084
func makeFakePods(ownerUid types.UID, samplePod *apiv1.Pod, podCount int) []*apiv1.Pod {
9185
var fakePods []*apiv1.Pod
9286
for i := 1; i <= podCount; i++ {
93-
newPod := withFakePodAnnotation(samplePod.DeepCopy())
87+
newPod := fake.WithFakePodAnnotation(samplePod.DeepCopy())
9488
newPod.Name = fmt.Sprintf("%s-copy-%d", samplePod.Name, i)
9589
newPod.UID = types.UID(fmt.Sprintf("%s-%d", string(ownerUid), i))
9690
newPod.Spec.NodeName = ""
@@ -99,16 +93,6 @@ func makeFakePods(ownerUid types.UID, samplePod *apiv1.Pod, podCount int) []*api
9993
return fakePods
10094
}
10195

102-
// withFakePodAnnotation adds annotation of key `FakePodAnnotationKey` with value `FakePodAnnotationValue` to passed pod.
103-
// withFakePodAnnotation also creates a new annotations map if original pod.Annotations is nil
104-
func withFakePodAnnotation(pod *apiv1.Pod) *apiv1.Pod {
105-
if pod.Annotations == nil {
106-
pod.Annotations = make(map[string]string, 1)
107-
}
108-
pod.Annotations[FakePodAnnotationKey] = FakePodAnnotationValue
109-
return pod
110-
}
111-
11296
// fakePodCount calculate the fake pod count that should be injected from this podGroup
11397
func (p *podGroup) fakePodCount() int {
11498
// Controllers with no unschedulable pods are ignored
@@ -142,14 +126,6 @@ func listControllers(ctx *context.AutoscalingContext) []controller {
142126
return controllers
143127
}
144128

145-
// IsFake returns true if the a pod is marked as fake and false otherwise
146-
func IsFake(pod *apiv1.Pod) bool {
147-
if pod.Annotations == nil {
148-
return false
149-
}
150-
return pod.Annotations[FakePodAnnotationKey] == FakePodAnnotationValue
151-
}
152-
153129
func (p *PodInjectionPodListProcessor) skipBackedoffControllers(controllers []controller) []controller {
154130
var filteredControllers []controller
155131
backoffRegistry := p.fakePodControllerBackoffRegistry

cluster-autoscaler/processors/podinjection/pod_injection_processor_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
podinjectionbackoff "k8s.io/autoscaler/cluster-autoscaler/processors/podinjection/backoff"
3333
"k8s.io/autoscaler/cluster-autoscaler/simulator/clustersnapshot/store"
3434
"k8s.io/autoscaler/cluster-autoscaler/simulator/clustersnapshot/testsnapshot"
35+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
3536
"k8s.io/autoscaler/cluster-autoscaler/simulator/framework"
3637
"k8s.io/autoscaler/cluster-autoscaler/utils/kubernetes"
3738
. "k8s.io/autoscaler/cluster-autoscaler/utils/test"
@@ -382,8 +383,7 @@ func TestMakeFakePods(t *testing.T) {
382383
assert.Equal(t, fakePod.Name, fmt.Sprintf("%s-copy-%d", samplePod.Name, idx+1))
383384
assert.Equal(t, fakePod.UID, types.UID(fmt.Sprintf("%s-%d", string(ownerUid), idx+1)))
384385
assert.Equal(t, "", fakePod.Spec.NodeName)
385-
assert.NotNil(t, fakePod.Annotations)
386-
assert.Equal(t, fakePod.Annotations[FakePodAnnotationKey], FakePodAnnotationValue)
386+
assert.True(t, fake.IsFake(fakePod))
387387
}
388388

389389
// Test case: Zero fake pod count

cluster-autoscaler/processors/podinjection/scale_up_status_processor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
ca_context "k8s.io/autoscaler/cluster-autoscaler/context"
2727
podinjectionbackoff "k8s.io/autoscaler/cluster-autoscaler/processors/podinjection/backoff"
2828
"k8s.io/autoscaler/cluster-autoscaler/processors/status"
29+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
2930
"k8s.io/klog/v2"
3031
)
3132

@@ -63,7 +64,7 @@ func filterFakePods[T any](podsWrappers []T, getPod func(T) *apiv1.Pod, resource
6364

6465
for _, podsWrapper := range podsWrappers {
6566
currentPod := getPod(podsWrapper)
66-
if !IsFake(currentPod) {
67+
if !fake.IsFake(currentPod) {
6768
filteredPodsSouces = append(filteredPodsSouces, podsWrapper)
6869
continue
6970
}
@@ -86,7 +87,7 @@ func filterFakePods[T any](podsWrappers []T, getPod func(T) *apiv1.Pod, resource
8687
func extractFakePodsControllersUIDs(NoScaleUpInfos []status.NoScaleUpInfo) map[types.UID]bool {
8788
uids := make(map[types.UID]bool)
8889
for _, NoScaleUpInfo := range NoScaleUpInfos {
89-
if IsFake(NoScaleUpInfo.Pod) {
90+
if fake.IsFake(NoScaleUpInfo.Pod) {
9091
uids[NoScaleUpInfo.Pod.UID] = true
9192
}
9293
}

cluster-autoscaler/processors/podinjection/scale_up_status_processor_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"k8s.io/autoscaler/cluster-autoscaler/context"
2525
podinjectionbackoff "k8s.io/autoscaler/cluster-autoscaler/processors/podinjection/backoff"
2626
"k8s.io/autoscaler/cluster-autoscaler/processors/status"
27+
"k8s.io/autoscaler/cluster-autoscaler/simulator/fake"
2728
. "k8s.io/autoscaler/cluster-autoscaler/utils/test"
2829
)
2930

@@ -82,7 +83,7 @@ func createPod(name string, isFake bool) *apiv1.Pod {
8283
if !isFake {
8384
return
8485
}
85-
*p = *withFakePodAnnotation(p)
86+
*p = *fake.WithFakePodAnnotation(p)
8687
})
8788
}
8889

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package fake
18+
19+
import (
20+
apiv1 "k8s.io/api/core/v1"
21+
)
22+
23+
const (
24+
// FakePodAnnotationKey the key for pod type.
25+
FakePodAnnotationKey = "podtype"
26+
// FakePodAnnotationValue the value for a fake pod,
27+
FakePodAnnotationValue = "fakepod"
28+
)
29+
30+
// IsFake returns true if the a pod is marked as fake and false otherwise,
31+
func IsFake(pod *apiv1.Pod) bool {
32+
if pod.Annotations == nil {
33+
return false
34+
}
35+
return pod.Annotations[FakePodAnnotationKey] == FakePodAnnotationValue
36+
}
37+
38+
// WithFakePodAnnotation adds annotation of key `FakePodAnnotationKey` with value `FakePodAnnotationValue` to passed pod.
39+
// WithFakePodAnnotation also creates a new annotations map if original pod.Annotations is nil.
40+
func WithFakePodAnnotation(pod *apiv1.Pod) *apiv1.Pod {
41+
if pod.Annotations == nil {
42+
pod.Annotations = make(map[string]string, 1)
43+
}
44+
pod.Annotations[FakePodAnnotationKey] = FakePodAnnotationValue
45+
return pod
46+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package fake
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
apiv1 "k8s.io/api/core/v1"
24+
"k8s.io/autoscaler/cluster-autoscaler/utils/test"
25+
)
26+
27+
func TestIsFake(t *testing.T) {
28+
testCases := []struct {
29+
name string
30+
pod *apiv1.Pod
31+
want bool
32+
}{
33+
{
34+
name: "real pod",
35+
pod: test.BuildTestPod("real", 10, 10),
36+
want: false,
37+
},
38+
{
39+
name: "fake pod",
40+
pod: WithFakePodAnnotation(test.BuildTestPod("fake", 10, 10)),
41+
want: true,
42+
},
43+
}
44+
for _, tc := range testCases {
45+
t.Run(tc.name, func(t *testing.T) {
46+
got := IsFake(tc.pod)
47+
assert.Equal(t, tc.want, got)
48+
})
49+
}
50+
}
51+
52+
func TestWithFakePodAnnotation(t *testing.T) {
53+
pod := test.BuildTestPod("pod", 10, 10)
54+
assert.Equal(t, map[string]string{}, pod.Annotations)
55+
pod = WithFakePodAnnotation(pod)
56+
assert.NotNil(t, pod.Annotations)
57+
assert.Equal(t, FakePodAnnotationValue, pod.Annotations[FakePodAnnotationKey])
58+
}

0 commit comments

Comments
 (0)