@@ -10,8 +10,8 @@ import (
1010
1111 corev1 "k8s.io/api/core/v1"
1212 schedulingv1 "k8s.io/api/scheduling/v1"
13- "k8s.io/apimachinery/pkg/api/resource"
1413 "k8s.io/apimachinery/pkg/runtime"
14+ resourcehelpers "k8s.io/component-helpers/resource"
1515 "sigs.k8s.io/controller-runtime/pkg/client"
1616 logf "sigs.k8s.io/controller-runtime/pkg/log"
1717 "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -24,15 +24,6 @@ import (
2424
2525var resizeLog = logf .Log .WithName ("pod-resize-validator" )
2626
27- func isPodResizeInfeasible (pod * corev1.Pod ) bool {
28- for _ , c := range pod .Status .Conditions {
29- if c .Type == corev1 .PodResizePending {
30- return c .Status == corev1 .ConditionTrue && c .Reason == corev1 .PodReasonInfeasible
31- }
32- }
33- return false
34- }
35-
3627// memoryLimitBytesPerUnit converts a Queue Memory.Limit (in megabytes) to bytes.
3728const memoryLimitBytesPerUnit = 1_000_000
3829
@@ -135,135 +126,40 @@ func (v *PodResizeValidator) validateResize(ctx context.Context, oldPod, newPod
135126// podResizeDelta computes the net per-resource increase this resize introduces
136127// relative to what the queue already accounts for.
137128//
138- // The queue's Status.Allocated is a pod-level sum of per-container effective
139- // requests, so the delta is computed at the same granularity:
129+ // All three aggregates come from the same upstream helper the scheduler uses in
130+ // getPodResourceRequest, so the delta baseline is guaranteed to match queue
131+ // accounting by construction rather than by parallel hand-written logic:
140132//
141- // 1. For each regular container and each restartable init container (sidecar),
142- // accumulate three pod-level sums: newSpecSum, oldSpecSum, effectiveOldSum.
143- // 2. Skip a resource if its pod-level spec is unchanged (newSpecSum[r] ==
144- // oldSpecSum[r]) — that resource was not part of this resize request and
145- // must not generate spurious delta even when an earlier infeasible attempt
146- // left an unresolved stale spec.
147- // 3. For changed resources, delta = max(0, newSpecSum[r] - effectiveOldSum[r]).
148- // Pod-level aggregation naturally handles CPU/memory moved between containers
149- // (redistribution produces zero net delta at the pod level).
133+ // - newSpec : the resize target (spec only)
134+ // - oldSpec : the pre-resize target (spec only)
135+ // - effectiveOld : what the queue currently charges, i.e. the KEP-1287
136+ // effective request max(spec, enacted, allocated), or max(enacted, allocated)
137+ // when the kubelet marked the resize Infeasible
150138//
151- // Effective old baseline per container:
152- // - If old pod has a current Infeasible condition: max(enacted, allocated).
153- // The infeasible spec was never committed by the kubelet, so the queue only
154- // reflects enacted/allocated.
155- // - Otherwise (normal / Deferred / InProgress): max(spec, enacted, allocated),
156- // matching what the scheduler charges for those states.
157- // - Falls back to old spec when no ContainerStatus is available.
139+ // A resource whose spec is unchanged is skipped: it is not part of this resize,
140+ // and an unresolved Infeasible spec on it must not produce phantom delta.
158141func podResizeDelta (oldPod , newPod * corev1.Pod ) corev1.ResourceList {
159- oldInfeasible := isPodResizeInfeasible (oldPod )
160-
161- statusByName := make (map [string ]* corev1.ContainerStatus , len (oldPod .Status .ContainerStatuses ))
162- for i := range oldPod .Status .ContainerStatuses {
163- statusByName [oldPod .Status .ContainerStatuses [i ].Name ] = & oldPod .Status .ContainerStatuses [i ]
164- }
165- oldByName := make (map [string ]* corev1.Container , len (oldPod .Spec .Containers ))
166- for i := range oldPod .Spec .Containers {
167- oldByName [oldPod .Spec .Containers [i ].Name ] = & oldPod .Spec .Containers [i ]
168- }
169-
170- initStatusByName := make (map [string ]* corev1.ContainerStatus , len (oldPod .Status .InitContainerStatuses ))
171- for i := range oldPod .Status .InitContainerStatuses {
172- initStatusByName [oldPod .Status .InitContainerStatuses [i ].Name ] = & oldPod .Status .InitContainerStatuses [i ]
173- }
174- oldInitByName := make (map [string ]* corev1.Container , len (oldPod .Spec .InitContainers ))
175- for i := range oldPod .Spec .InitContainers {
176- oldInitByName [oldPod .Spec .InitContainers [i ].Name ] = & oldPod .Spec .InitContainers [i ]
177- }
142+ specOnly := resourcehelpers.PodResourcesOptions {}
143+ withStatus := resourcehelpers.PodResourcesOptions {UseStatusResources : true }
178144
179- newSpecSum := corev1. ResourceList {}
180- oldSpecSum := corev1. ResourceList {}
181- effectiveOldSum := corev1. ResourceList {}
145+ newSpec := resourcehelpers . AggregateContainerRequests ( newPod , specOnly )
146+ oldSpec := resourcehelpers . AggregateContainerRequests ( oldPod , specOnly )
147+ effectiveOld := resourcehelpers . AggregateContainerRequests ( oldPod , withStatus )
182148
183- for i := range newPod .Spec .Containers {
184- accumulateDeltaSums (& newPod .Spec .Containers [i ], oldByName , statusByName , oldInfeasible , newSpecSum , oldSpecSum , effectiveOldSum )
185- }
186- for i := range newPod .Spec .InitContainers {
187- c := & newPod .Spec .InitContainers [i ]
188- if c .RestartPolicy == nil || * c .RestartPolicy != corev1 .ContainerRestartPolicyAlways {
189- continue
190- }
191- accumulateDeltaSums (c , oldInitByName , initStatusByName , oldInfeasible , newSpecSum , oldSpecSum , effectiveOldSum )
192- }
193-
194- zero := resource .MustParse ("0" )
195149 delta := corev1.ResourceList {}
196- for r , newQty := range newSpecSum {
197- if oldQty , ok := oldSpecSum [ r ]; ok && newQty .Cmp (oldQty ) == 0 {
198- continue // resource not changed by this resize at the pod level
150+ for resName , newQty := range newSpec {
151+ if oldQty , ok := oldSpec [ resName ]; ok && newQty .Cmp (oldQty ) == 0 {
152+ continue // resource not changed by this resize
199153 }
200- effectiveOld := effectiveOldSum [r ]
201154 diff := newQty .DeepCopy ()
202- diff .Sub (effectiveOld )
203- if diff .Cmp ( zero ) > 0 {
204- delta [r ] = diff
155+ diff .Sub (effectiveOld [ resName ] )
156+ if diff .Sign ( ) > 0 {
157+ delta [resName ] = diff
205158 }
206159 }
207160 return delta
208161}
209162
210- // accumulateDeltaSums accumulates one container's contribution into three pod-level
211- // sums. The effective old baseline follows the queue's own accounting:
212- // - infeasible old pod: max(enacted, allocated)
213- // - otherwise: max(old_spec, enacted, allocated)
214- func accumulateDeltaSums (
215- newC * corev1.Container ,
216- oldByName map [string ]* corev1.Container ,
217- statusByName map [string ]* corev1.ContainerStatus ,
218- oldInfeasible bool ,
219- newSpecSum , oldSpecSum , effectiveOldSum corev1.ResourceList ,
220- ) {
221- oldC := oldByName [newC .Name ]
222- cs := statusByName [newC .Name ]
223-
224- for resName , newQty := range newC .Resources .Requests {
225- cur := newSpecSum [resName ]
226- cur .Add (newQty )
227- newSpecSum [resName ] = cur
228-
229- var oldSpecQty resource.Quantity
230- if oldC != nil {
231- oldSpecQty = oldC .Resources .Requests [resName ]
232- }
233- cur = oldSpecSum [resName ]
234- cur .Add (oldSpecQty )
235- oldSpecSum [resName ] = cur
236-
237- effectiveOld := oldSpecQty
238- if cs != nil {
239- candidates := []resource.Quantity {}
240- if ! oldInfeasible {
241- candidates = append (candidates , oldSpecQty )
242- }
243- if cs .Resources != nil {
244- if enacted , ok := cs .Resources .Requests [resName ]; ok {
245- candidates = append (candidates , enacted )
246- }
247- }
248- if alloc , ok := cs .AllocatedResources [resName ]; ok {
249- candidates = append (candidates , alloc )
250- }
251- if len (candidates ) > 0 {
252- best := candidates [0 ]
253- for _ , q := range candidates [1 :] {
254- if q .Cmp (best ) > 0 {
255- best = q
256- }
257- }
258- effectiveOld = best
259- }
260- }
261- cur = effectiveOldSum [resName ]
262- cur .Add (effectiveOld )
263- effectiveOldSum [resName ] = cur
264- }
265- }
266-
267163func checkQueueCapacity (
268164 queue * v2.Queue ,
269165 delta corev1.ResourceList ,
0 commit comments