You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@yunikorn.apache.org by ma...@apache.org on 2026/09/24 05:59:38 UTC

(yunikorn-k8shim) branch master updated: [YUNIKORN-3472] Consistent appid and user checking (#1103)

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 8a670bf9 [YUNIKORN-3472] Consistent appid and user checking (#1103)
8a670bf9 is described below

commit 8a670bf9ad0d3d98c50f81d7a64e67b21b4dd729
Author: Wilfred Spiegelenburg <wi...@apache.org>
AuthorDate: Thu Sep 24 11:29:20 2026 +0530

    [YUNIKORN-3472] Consistent appid and user checking (#1103)
    
    Application ID and user info checks should be consistent for creates and
    updates of a pod in both the admission controller and the k8shim itself.
    Use the same function in both admission controller and k8shim to build
    the application ID for a pod instead of separate logic.
    
    Closes: #1103
    
    Signed-off-by: Manikandan R <ma...@gmail.com>
---
 pkg/admission/admission_controller.go      |  45 ++++---
 pkg/admission/admission_controller_test.go | 184 +++++++++++++++++++++++------
 pkg/admission/util.go                      |   8 ++
 pkg/admission/util_test.go                 |  26 ++++
 pkg/common/utils/utils.go                  |  45 +++----
 5 files changed, 241 insertions(+), 67 deletions(-)

diff --git a/pkg/admission/admission_controller.go b/pkg/admission/admission_controller.go
index 7e68efd1..4067fb95 100644
--- a/pkg/admission/admission_controller.go
+++ b/pkg/admission/admission_controller.go
@@ -166,6 +166,23 @@ func (c *AdmissionController) processPod(req *admissionv1.AdmissionRequest, name
 		return admissionResponseBuilder(uid, false, err.Error(), nil)
 	}
 
+	if namespace == schedulerconf.GetSchedulerNamespace() {
+		if utils.GetPodLabelValue(&pod, constants.LabelApp) == yunikornPod {
+			if pod.Spec.SchedulerName == constants.SchedulerName {
+				log.Log(log.Admission).Info("YuniKorn pod has self as scheduler")
+				return admissionResponseBuilder(uid, false, "YuniKorn cannot schedule itself", nil)
+			}
+			log.Log(log.Admission).Info("YuniKorn pod, ignoring")
+			return admissionResponseBuilder(uid, true, "", nil)
+		}
+		log.Log(log.Admission).Info("Non YuniKorn pod in yunikorn namespace", zap.String("UID", uid))
+	}
+
+	if !c.shouldProcessNamespace(namespace) {
+		log.Log(log.Admission).Info("bypassing namespace", zap.String("namespace", namespace))
+		return admissionResponseBuilder(uid, true, "", nil)
+	}
+
 	userName := req.UserInfo.Username
 	groups := req.UserInfo.Groups
 	failureResponse, userInfoSet := c.checkUserInfoAnnotation(func() (string, bool) {
@@ -185,17 +202,6 @@ func (c *AdmissionController) processPod(req *admissionv1.AdmissionRequest, name
 		patch = append(patch, *patchOp)
 	}
 
-	if labelAppValue := utils.GetPodLabelValue(&pod, constants.LabelApp); labelAppValue != "" {
-		if labelAppValue == yunikornPod {
-			log.Log(log.Admission).Info("ignore yunikorn pod")
-			return admissionResponseBuilder(uid, true, "", nil)
-		}
-	}
-
-	if !c.shouldProcessNamespace(namespace) {
-		log.Log(log.Admission).Info("bypassing namespace", zap.String("namespace", namespace))
-		return admissionResponseBuilder(uid, true, "", nil)
-	}
 	patch = updateSchedulerName(patch)
 
 	if c.shouldLabelNamespace(namespace) {
@@ -296,11 +302,17 @@ func (c *AdmissionController) processPodUpdate(req *admissionv1.AdmissionRequest
 		return admissionResponseBuilder(uid, false, err.Error(), nil)
 	}
 
-	if labelAppValue, ok := newPod.Labels[constants.LabelApp]; ok {
-		if labelAppValue == yunikornPod {
-			log.Log(log.Admission).Info("pod update - ignore yunikorn pod")
+	if namespace == schedulerconf.GetSchedulerNamespace() {
+		wasYuniKorn := utils.GetPodLabelValue(&oldPod, constants.LabelApp) == yunikornPod
+		isYuniKorn := utils.GetPodLabelValue(&newPod, constants.LabelApp) == yunikornPod
+		if isYuniKorn != wasYuniKorn {
+			return admissionResponseBuilder(uid, false, "YuniKorn app label change not allowed", nil)
+		}
+		if isYuniKorn && wasYuniKorn {
+			log.Log(log.Admission).Info("YuniKorn pod, ignoring")
 			return admissionResponseBuilder(uid, true, "", nil)
 		}
+		log.Log(log.Admission).Info("Non YuniKorn pod in scheduler namespace", zap.String("UID", uid))
 	}
 
 	if !c.shouldProcessAdmissionReview(namespace, newPod.Labels) {
@@ -315,9 +327,14 @@ func (c *AdmissionController) processPodUpdate(req *admissionv1.AdmissionRequest
 		zap.String("new", newUserInfo))
 
 	if originalUserInfo != newUserInfo {
+		log.Log(log.Admission).Info("pod update - userinfo annotation change not allowed", zap.String("UID", uid))
 		return admissionResponseBuilder(uid, false, "user info annotation change is not allowed", nil)
 	}
 
+	if changedAppID(&oldPod, &newPod) {
+		log.Log(log.Admission).Info("pod update - application ID change not allowed", zap.String("UID", uid))
+		return admissionResponseBuilder(uid, false, "applicationID change is not allowed", nil)
+	}
 	return admissionResponseBuilder(uid, true, "", nil)
 }
 
diff --git a/pkg/admission/admission_controller_test.go b/pkg/admission/admission_controller_test.go
index 99d97858..6d73828c 100644
--- a/pkg/admission/admission_controller_test.go
+++ b/pkg/admission/admission_controller_test.go
@@ -39,6 +39,7 @@ import (
 	"github.com/apache/yunikorn-k8shim/pkg/admission/common"
 	"github.com/apache/yunikorn-k8shim/pkg/admission/conf"
 	"github.com/apache/yunikorn-k8shim/pkg/common/constants"
+	schedulerconf "github.com/apache/yunikorn-k8shim/pkg/conf"
 )
 
 type responseMode int
@@ -444,22 +445,40 @@ func errorResponseMock(w http.ResponseWriter, r *http.Request) {
 	w.Write([]byte(resp)) //nolint:errcheck
 }
 
-// nolint: funlen
-func TestMutate(t *testing.T) {
-	var ac *AdmissionController
-	var pod v1.Pod
-	var req *admissionv1.AdmissionRequest
-	var resp *admissionv1.AdmissionResponse
-	var podJSON []byte
-	var err error
-
-	ac = prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", false, true)
+func TestMutateYuniKorn(t *testing.T) {
+	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", false, true)
 
 	// nil request
-	resp = ac.mutate(nil)
+	resp := ac.mutate(nil)
 	assert.Check(t, !resp.Allowed, "response allowed with nil request")
 
 	// yunikorn pod
+	pod := v1.Pod{ObjectMeta: metav1.ObjectMeta{
+		Namespace: schedulerconf.GetSchedulerNamespace(),
+		Labels:    map[string]string{"app": "yunikorn"},
+	}}
+	req := &admissionv1.AdmissionRequest{
+		UID:       "test-uid",
+		Namespace: schedulerconf.GetSchedulerNamespace(),
+		Kind:      metav1.GroupVersionKind{Kind: "Pod"},
+	}
+	podJSON, err := json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.Object = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, resp.Allowed, "response not allowed for yunikorn pod")
+	assert.Equal(t, len(resp.Patch), 0, "non-empty patch for yunikorn pod")
+
+	// self schedule
+	pod.Spec.SchedulerName = constants.SchedulerName
+	podJSON, err = json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.Object = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, !resp.Allowed, "response not allowed for yunikorn pod")
+	assert.Equal(t, len(resp.Patch), 0, "non-empty patch for yunikorn pod")
+
+	// yunikorn app tagged pod not in NS
 	pod = v1.Pod{ObjectMeta: metav1.ObjectMeta{
 		Namespace: "test-ns",
 		Labels:    map[string]string{"app": "yunikorn"},
@@ -473,22 +492,28 @@ func TestMutate(t *testing.T) {
 	assert.NilError(t, err, "failed to marshal pod")
 	req.Object = runtime.RawExtension{Raw: podJSON}
 	resp = ac.mutate(req)
-	assert.Check(t, resp.Allowed, "response not allowed for yunikorn pod")
-	assert.Equal(t, len(resp.Patch), 0, "non-empty patch for yunikorn pod")
+	assert.Check(t, resp.Allowed, "response not allowed for yunikorn tagged pod")
+	assert.Equal(t, schedulerName(t, resp.Patch), "yunikorn", "yunikorn not set as scheduler for pod")
+	assert.Equal(t, labels(t, resp.Patch)[constants.LabelApplicationID], "yunikorn-test-ns-autogen", "wrong applicationId label")
+	assert.Equal(t, labels(t, resp.Patch)[constants.LabelQueueName], nil, "incorrect queue name")
+}
+
+func TestMutatePod(t *testing.T) {
+	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", false, true)
 
 	// pod with empty namespace should be scheduled to 'default' namespace
-	pod = v1.Pod{ObjectMeta: metav1.ObjectMeta{
+	pod := v1.Pod{ObjectMeta: metav1.ObjectMeta{
 		Namespace: "",
 	}}
-	req = &admissionv1.AdmissionRequest{
+	req := &admissionv1.AdmissionRequest{
 		UID:       "test-uid",
 		Namespace: "",
 		Kind:      metav1.GroupVersionKind{Kind: "Pod"},
 	}
-	podJSON, err = json.Marshal(pod)
+	podJSON, err := json.Marshal(pod)
 	assert.NilError(t, err, "failed to marshal pod")
 	req.Object = runtime.RawExtension{Raw: podJSON}
-	resp = ac.mutate(req)
+	resp := ac.mutate(req)
 	assert.Check(t, resp.Allowed, "response not allowed for pod")
 	assert.Equal(t, schedulerName(t, resp.Patch), "yunikorn", "yunikorn not set as scheduler for pod")
 	assert.Equal(t, labels(t, resp.Patch)[constants.LabelApplicationID], "yunikorn-default-autogen", "wrong applicationId label")
@@ -570,7 +595,10 @@ func TestMutate(t *testing.T) {
 	resp = ac.mutate(req)
 	assert.Check(t, resp.Allowed, "response not allowed for unknown object type")
 	assert.Equal(t, len(resp.Patch), 0, "non-empty patch for unknown object type")
+}
 
+func TestMutateObject(t *testing.T) {
+	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", false, true)
 	// deployment - annotation is set
 	deployment := &appsv1.Deployment{
 		Spec: appsv1.DeploymentSpec{
@@ -583,7 +611,7 @@ func TestMutate(t *testing.T) {
 			},
 		},
 	}
-	req = &admissionv1.AdmissionRequest{
+	req := &admissionv1.AdmissionRequest{
 		UID:       "test-uid",
 		Namespace: "test-ns",
 		Kind:      metav1.GroupVersionKind{Kind: "Deployment"},
@@ -591,11 +619,10 @@ func TestMutate(t *testing.T) {
 			Username: "testExtUser",
 		},
 	}
-	var deploymentJSON []byte
-	deploymentJSON, err = json.Marshal(deployment)
+	deploymentJSON, err := json.Marshal(deployment)
 	assert.NilError(t, err, "failed to marshal deployment")
 	req.Object = runtime.RawExtension{Raw: deploymentJSON}
-	resp = ac.mutate(req)
+	resp := ac.mutate(req)
 	assert.Check(t, resp.Allowed, "response not allowed for unknown object type")
 	assert.Equal(t, len(resp.Patch), 0, "non-empty patch for deployment")
 
@@ -650,31 +677,24 @@ func TestMutate(t *testing.T) {
 }
 
 func TestMutateUpdate(t *testing.T) {
-	var ac *AdmissionController
-	var pod v1.Pod
-	var req *admissionv1.AdmissionRequest
-	var resp *admissionv1.AdmissionResponse
-	var podJSON []byte
-	var err error
-
-	ac = prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", true, true)
+	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", true, true)
 
 	// nil request
-	resp = ac.mutate(nil)
+	resp := ac.mutate(nil)
 	assert.Check(t, !resp.Allowed, "response allowed with nil request")
 
 	// yunikorn pod
-	pod = v1.Pod{ObjectMeta: metav1.ObjectMeta{
+	pod := v1.Pod{ObjectMeta: metav1.ObjectMeta{
 		Namespace: "test-ns",
 		Labels:    map[string]string{"app": "yunikorn"},
 	}}
-	req = &admissionv1.AdmissionRequest{
+	req := &admissionv1.AdmissionRequest{
 		UID:       "test-uid",
 		Namespace: "test-ns",
 		Kind:      metav1.GroupVersionKind{Kind: "Pod"},
 		Operation: admissionv1.Update,
 	}
-	podJSON, err = json.Marshal(pod)
+	podJSON, err := json.Marshal(pod)
 	assert.NilError(t, err, "failed to marshal pod")
 	req.Object = runtime.RawExtension{Raw: podJSON}
 	req.OldObject = runtime.RawExtension{Raw: podJSON}
@@ -746,6 +766,104 @@ func TestMutateUpdate(t *testing.T) {
 	assert.Check(t, resp.Allowed, "response was not allowed")
 }
 
+func TestMutateUser(t *testing.T) {
+	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", true, true)
+
+	// nil request
+	resp := ac.mutate(nil)
+	assert.Check(t, !resp.Allowed, "response allowed with nil request")
+
+	// normal pod, mutate user info not allowed
+	pod := v1.Pod{ObjectMeta: metav1.ObjectMeta{
+		Namespace:   schedulerconf.GetSchedulerNamespace(),
+		Annotations: map[string]string{common.UserInfoAnnotation: validUserInfoAnnotation},
+	}}
+	oldPod := v1.Pod{ObjectMeta: metav1.ObjectMeta{
+		Namespace: schedulerconf.GetSchedulerNamespace(),
+	}}
+	req := &admissionv1.AdmissionRequest{
+		UID:       "test-uid",
+		Namespace: schedulerconf.GetSchedulerNamespace(),
+		Kind:      metav1.GroupVersionKind{Kind: "Pod"},
+		Operation: admissionv1.Update,
+	}
+	// non yunikorn pod cannot change
+	podJSON, err := json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.Object = runtime.RawExtension{Raw: podJSON}
+	podJSON, err = json.Marshal(oldPod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.OldObject = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, !resp.Allowed, "response was allowed")
+
+	// change yunikorn pod blocks the change
+	oldPod.Labels = map[string]string{"app": constants.SchedulerName}
+	podJSON, err = json.Marshal(oldPod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.OldObject = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, !resp.Allowed, "response was allowed")
+
+	// yunikorn pod ignore user change
+	pod.Labels = map[string]string{"app": constants.SchedulerName}
+	podJSON, err = json.Marshal(oldPod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.Object = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, resp.Allowed, "response was not allowed")
+}
+
+func TestMutateAppID(t *testing.T) {
+	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", true, true)
+
+	// nil request
+	resp := ac.mutate(nil)
+	assert.Check(t, !resp.Allowed, "response allowed with nil request")
+
+	// app-1 pod label value change
+	pod := v1.Pod{ObjectMeta: metav1.ObjectMeta{
+		Namespace: "test-ns",
+		Labels:    map[string]string{constants.CanonicalLabelApplicationID: "app-1"},
+	}}
+	req := &admissionv1.AdmissionRequest{
+		UID:       "test-uid",
+		Namespace: "test-ns",
+		Kind:      metav1.GroupVersionKind{Kind: "Pod"},
+		Operation: admissionv1.Update,
+	}
+	podJSON, err := json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.Object = runtime.RawExtension{Raw: podJSON}
+	pod.Labels = map[string]string{constants.CanonicalLabelApplicationID: "app-2"}
+	podJSON, err = json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.OldObject = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, !resp.Allowed, "response allowed for appID mutation")
+
+	// app-1 pod adding canonical label overrides app annotation
+	pod = v1.Pod{ObjectMeta: metav1.ObjectMeta{
+		Namespace:   "test-ns",
+		Annotations: map[string]string{constants.AnnotationApplicationID: "app-1"},
+	}}
+	req = &admissionv1.AdmissionRequest{
+		UID:       "test-uid",
+		Namespace: "test-ns",
+		Kind:      metav1.GroupVersionKind{Kind: "Pod"},
+		Operation: admissionv1.Update,
+	}
+	podJSON, err = json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.Object = runtime.RawExtension{Raw: podJSON}
+	pod.Labels = map[string]string{constants.CanonicalLabelApplicationID: "app-2"}
+	podJSON, err = json.Marshal(pod)
+	assert.NilError(t, err, "failed to marshal pod")
+	req.OldObject = runtime.RawExtension{Raw: podJSON}
+	resp = ac.mutate(req)
+	assert.Check(t, !resp.Allowed, "response allowed for appID mutation")
+}
+
 func TestExternalAuthentication(t *testing.T) {
 	ac := prepareController(t, "", "", "^kube-system$,^bypass$", "", "^nolabel$", false, true)
 
diff --git a/pkg/admission/util.go b/pkg/admission/util.go
index d25217d0..5fe86486 100644
--- a/pkg/admission/util.go
+++ b/pkg/admission/util.go
@@ -81,3 +81,11 @@ func convert2Namespace(obj interface{}) *v1.Namespace {
 	log.Log(log.AdmissionUtils).Warn("cannot convert to *v1.Namespace", zap.Stringer("type", reflect.TypeOf(obj)))
 	return nil
 }
+
+// changedAppID returns true if the application ID has changed in an update
+func changedAppID(oldPod, newPod *v1.Pod) bool {
+	oldID := utils.GetApplicationIDValue(oldPod)
+	newID := utils.GetApplicationIDValue(newPod)
+
+	return oldID != newID
+}
diff --git a/pkg/admission/util_test.go b/pkg/admission/util_test.go
index 2a162d76..3490194b 100644
--- a/pkg/admission/util_test.go
+++ b/pkg/admission/util_test.go
@@ -205,3 +205,29 @@ func TestConvert2Namespace(t *testing.T) {
 	result = convert2Namespace(pod)
 	assert.Check(t, result == nil)
 }
+
+func Test_changedAppID(t *testing.T) {
+	tests := []struct {
+		name   string
+		oldPod *v1.Pod
+		newPod *v1.Pod
+		want   bool
+	}{
+		{"no appID", &v1.Pod{ObjectMeta: metav1.ObjectMeta{}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{}}, false},
+		{"appID no change", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.LabelApplicationID: "app1"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.LabelApplicationID: "app1"}}}, false},
+		{"appID label val change", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.LabelApplicationID: "app1"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.LabelApplicationID: "app2"}}}, true},
+		{"appID canonical add", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.LabelApplicationID: "app1"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.CanonicalLabelApplicationID: "app2", constants.LabelApplicationID: "app1"}}}, true},
+		{"appID label remove", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.CanonicalLabelApplicationID: "app1", constants.LabelApplicationID: "app1"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{}}, true},
+		{"appID annotation add", &v1.Pod{ObjectMeta: metav1.ObjectMeta{}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AnnotationApplicationID: "app3"}}}, true},
+		{"appID annotation val change", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AnnotationApplicationID: "app4"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AnnotationApplicationID: "app3"}}}, true},
+		{"appID same value add", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.LabelApplicationID: "app1"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.CanonicalLabelApplicationID: "app1", constants.LabelApplicationID: "app1"}}}, false},
+		{"appID label fallback", &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{constants.CanonicalLabelApplicationID: "app1"}, Annotations: map[string]string{constants.AnnotationApplicationID: "app4"}}}, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{constants.AnnotationApplicationID: "app4"}}}, true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := changedAppID(tt.oldPod, tt.newPod); got != tt.want {
+				t.Errorf("changedAppID() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/common/utils/utils.go b/pkg/common/utils/utils.go
index 4522da2e..9c780940 100644
--- a/pkg/common/utils/utils.go
+++ b/pkg/common/utils/utils.go
@@ -144,14 +144,30 @@ func GetApplicationIDFromPod(pod *v1.Pod) string {
 	if strings.Compare(pod.Spec.SchedulerName, constants.SchedulerName) != 0 {
 		return ""
 	}
+	appID := GetApplicationIDValue(pod)
 
-	// Application ID can be defined in multiple places
-	// The application ID is determined by the following order.
-	// 1. Label: constants.CanonicalLabelApplicationID
-	// 2. Annotation: constants.AnnotationApplicationID
-	// 3. Label: constants.LabelApplicationID
-	// 4. Label: constants.SparkLabelAppID
+	// does appID end with '-uniqueautogen' if it does replace suffix with pod UID
+	if before, ok := strings.CutSuffix(appID, uniqueAutogenSuffix); ok {
+		appID = fmt.Sprintf("%s-%s", before, string(pod.UID))
+	}
+
+	// if app ID is not empty, return it
+	if appID != "" {
+		return appID
+	}
+
+	// Standard deployment mode, so we need a valid Application ID to proceed. Generate one now.
+	return GenerateApplicationID(pod.Namespace, conf.GetSchedulerConf().GenerateUniqueAppIds, string(pod.UID))
+}
 
+// GetApplicationIDValue retrieves the application ID from the pod based on the predefined order
+// Application ID can be defined in multiple places
+// The application ID is determined by the following order.
+// 1. Label: constants.CanonicalLabelApplicationID
+// 2. Annotation: constants.AnnotationApplicationID
+// 3. Label: constants.LabelApplicationID
+// 4. Label: constants.SparkLabelAppID
+func GetApplicationIDValue(pod *v1.Pod) string {
 	appID := GetPodLabelValue(pod, constants.CanonicalLabelApplicationID)
 
 	if appID == "" {
@@ -170,22 +186,11 @@ func GetApplicationIDFromPod(pod *v1.Pod) string {
 			}
 		}
 	}
-
-	// does appID end with '-uniqueautogen'?
-	if strings.HasSuffix(appID, uniqueAutogenSuffix) {
-		// replace suffix with pod UID
-		appID = fmt.Sprintf("%s-%s", strings.TrimSuffix(appID, uniqueAutogenSuffix), string(pod.UID))
-	}
-
-	// if app ID is not empty, return it
-	if appID != "" {
-		return appID
-	}
-
-	// Standard deployment mode, so we need a valid Application ID to proceed. Generate one now.
-	return GenerateApplicationID(pod.Namespace, conf.GetSchedulerConf().GenerateUniqueAppIds, string(pod.UID))
+	return appID
 }
 
+// GetIgnoredLabelAnnotationInPod returns the labels and annotations keys and values that set a different value than
+// the one currently used. Reporting inconsistencies for values that can be set in multiple ways.
 func GetIgnoredLabelAnnotationInPod(pod *v1.Pod, currentValue string, labelKeys []string, annotationKeys []string) (map[string]string, map[string]string) {
 	ignoredLabel := make(map[string]string, 0)
 	ignoredAnnotation := make(map[string]string, 0)


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@yunikorn.apache.org
For additional commands, e-mail: issues-help@yunikorn.apache.org