This is an automated email from the ASF dual-hosted git repository.
manirajv06 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/yunikorn-k8shim.git
The following commit(s) were added to refs/heads/master by this push:
new edef7f7e [YUNIKORN-3381] Do not adopt terminated orphaned foreign pods
on node registration (#1088)
edef7f7e is described below
commit edef7f7edca02094de9f40571e7e2372e4260886
Author: hedger9487 <[email protected]>
AuthorDate: Mon Sep 7 13:40:31 2026 +0530
[YUNIKORN-3381] Do not adopt terminated orphaned foreign pods on node
registration (#1088)
Closes: #1088
Signed-off-by: Manikandan R <[email protected]>
---
pkg/cache/context.go | 14 ++++----
pkg/cache/context_test.go | 86 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 8 deletions(-)
diff --git a/pkg/cache/context.go b/pkg/cache/context.go
index 9ce9e6a5..68e54fec 100644
--- a/pkg/cache/context.go
+++ b/pkg/cache/context.go
@@ -454,20 +454,18 @@ func (ctx *Context) updateForeignPod(oldPod, pod *v1.Pod)
{
return
}
- // conditions for release:
- // 1. pod was previously assigned
- // 2. pod is now in a terminated state
- // 3. pod references a known node
+ // When an assigned foreign pod terminates:
+ // 1. Always remove it from schedulerCache to prevent phantom
adoption if the node registers later.
+ // 2. Only trigger allocation release in the core if the pod was
assigned to a known node (not orphaned).
if oldPod != nil && utils.IsPodTerminated(pod) {
- if !ctx.schedulerCache.IsPodOrphaned(string(pod.UID)) {
+ isOrphaned := ctx.schedulerCache.IsPodOrphaned(string(pod.UID))
+ ctx.schedulerCache.RemovePod(pod)
+ if !isOrphaned {
log.Log(log.ShimContext).Debug("pod terminated, trigger
foreign resource update",
zap.String("namespace", pod.Namespace),
zap.String("podName", pod.Name),
zap.String("podStatusBefore", podStatusBefore),
zap.String("podStatusCurrent",
string(pod.Status.Phase)))
- // this means pod is terminated
- // remove from the scheduler cache and create release
request to remove foreign allocation from the core
- ctx.schedulerCache.RemovePod(pod)
releaseReq :=
common.CreateReleaseRequestForForeignPod(string(pod.UID),
constants.DefaultPartition)
if err :=
ctx.apiProvider.GetAPIs().SchedulerAPI.UpdateAllocation(releaseReq); err != nil
{
log.Log(log.ShimContext).Error("failed to
remove foreign allocation from the core",
diff --git a/pkg/cache/context_test.go b/pkg/cache/context_test.go
index 384f1b49..07a534d2 100644
--- a/pkg/cache/context_test.go
+++ b/pkg/cache/context_test.go
@@ -2684,3 +2684,89 @@ func assertListerPods(pods []*v1.Pod, count int) bool {
}
return count == counted
}
+
+func TestTerminatedOrphanedForeignPodNotAdopted(t *testing.T) {
+ testCases := []struct {
+ name string
+ terminalPhase v1.PodPhase
+ }{
+ {"Succeeded", v1.PodSucceeded},
+ {"Failed", v1.PodFailed},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ context, apiProvider :=
initContextAndAPIProviderForTest()
+ dispatcher.Start()
+ defer dispatcher.UnregisterAllEventHandlers()
+ defer dispatcher.Stop()
+
+ apiProvider.MockSchedulerAPIUpdateNodeFn(func(request
*si.NodeRequest) error {
+ for _, node := range request.Nodes {
+
dispatcher.Dispatch(CachedSchedulerNodeEvent{
+ NodeID: node.NodeID,
+ Event: NodeAccepted,
+ })
+ }
+ return nil
+ })
+
+ // 1. Add two foreign pods assigned to Host1 before
Host1 is added:
+ // - pod 1: will terminate while orphaned
+ // - pod 2: remains running (live control)
+ deadPod := newPodHelper("dead-foreign-pod", "default",
uid1, Host1, "", v1.PodRunning)
+ deadPod.Spec.SchedulerName = "default-scheduler"
+ context.AddPod(deadPod)
+
+ livePod := newPodHelper("live-foreign-pod", "default",
uid2, Host1, "", v1.PodRunning)
+ livePod.Spec.SchedulerName = "default-scheduler"
+ context.AddPod(livePod)
+
+ assert.Assert(t,
context.schedulerCache.IsPodOrphaned(uid1), "deadPod should be marked as
orphaned")
+ assert.Assert(t,
context.schedulerCache.IsPodOrphaned(uid2), "livePod should be marked as
orphaned")
+ assert.Assert(t, context.schedulerCache.GetPod(uid1) !=
nil, "deadPod should be in cache")
+ assert.Assert(t, context.schedulerCache.GetPod(uid2) !=
nil, "livePod should be in cache")
+ assert.Equal(t,
apiProvider.GetSchedulerAPIUpdateAllocationCount(), int32(0))
+
+ // 2. deadPod terminates while still orphaned
+ deadPodTerminated := deadPod.DeepCopy()
+ deadPodTerminated.Status.Phase = tc.terminalPhase
+ context.UpdatePod(deadPod, deadPodTerminated)
+
+ // Invariant: Terminated orphaned pod must be purged;
live orphaned pod must remain
+ assert.Assert(t, context.schedulerCache.GetPod(uid1) ==
nil, "terminated orphaned pod must be removed from cache")
+ assert.Assert(t,
!context.schedulerCache.IsPodOrphaned(uid1), "terminated orphaned pod must not
remain in orphan map")
+ assert.Assert(t, context.schedulerCache.GetPod(uid2) !=
nil, "live orphaned pod must remain in cache")
+ assert.Assert(t,
context.schedulerCache.IsPodOrphaned(uid2), "live orphaned pod must remain in
orphan map")
+ assert.Equal(t,
apiProvider.GetSchedulerAPIUpdateAllocationCount(), int32(0),
+ "orphaned pod termination must not send release
request to core")
+
+ // 3. Node Host1 is now registered
+ node := v1.Node{
+ ObjectMeta: apis.ObjectMeta{
+ Name: Host1,
+ Namespace: "default",
+ UID: uid1,
+ },
+ Status: v1.NodeStatus{
+ Allocatable:
map[v1.ResourceName]resource.Quantity{
+ v1.ResourceCPU:
resource.MustParse("2"),
+ v1.ResourceMemory:
resource.MustParse("2048Mi"),
+ },
+ },
+ }
+ context.addNode(&node)
+
+ // Invariant: Core must receive EXACTLY 1 allocation
(for the live pod), not the dead pod
+ assert.Equal(t,
apiProvider.GetSchedulerAPIUpdateAllocationCount(), int32(1),
+ "only the live orphaned pod must be adopted on
node registration")
+
+ // Invariant: The shim NodeInfo predicate cache must
ONLY contain the live pod
+ nodeInfo := context.schedulerCache.GetNode(Host1)
+ assert.Assert(t, nodeInfo != nil, "node must exist in
scheduler cache")
+ pods := nodeInfo.GetPods()
+ assert.Equal(t, len(pods), 1, "nodeInfo should only
contain the live adopted pod")
+ assert.Equal(t, pods[0].GetPod().UID, types.UID(uid2),
"nodeInfo pod must match livePod UID")
+ })
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]