Repository: apecloud/kubeblocks

Issue:
[BUG]VerticalScaling Ops never finished with Cluster using InstanceTemplate
**Describe the bug**
Applying a VerticalScaling Ops with the whole component, when Cluster is using InstanceTemplate, all pods can finally updated and Cluster is already running, but Ops is still running and never Succeed.

**To Reproduce**
Steps to reproduce the behavior:
1. Creating a Cluster using InstanceTemplate, likes below
```
apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
  annotations:
    kubeblocks.io/crd-api-version: apps.kubeblocks.io/v1
  labels:
    clusterdefinition.kubeblocks.io/name: vip-mysql
  name: t-vip-mysql57-xaza
  namespace: thomas-demo
spec:
  clusterDef: vip-mysql
  componentSpecs:
  - componentDef: vip-mysql-5.7-0.9.0-beta.9
    env:
    - name: NODE_IP
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: spec.nodeName
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    - name: POD_IP
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: status.podIP
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.namespace
    - name: POD_UID
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.uid
    - name: MERCURY_CONTAINER_HOSTNAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    - name: MYSQL_PORT
      value: "3307"
    instanceUpdateStrategy:
      type: OnDelete
    instances:
    - env:
      - name: POOL_ID
        value: "1704819"
      - name: POOL_NAME
        value: yua0
      name: yua0
      ordinals:
        ranges:
        - end: 299
          start: 0
      replicas: 2
      schedulingPolicy:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: topology.kubernetes.io/zone
                  operator: In
                  values:
                  - cn-guangzhou-a
    - env:
      - name: POOL_ID
        value: "1704820"
      - name: POOL_NAME
        value: rim1
      name: rim1
      ordinals:
        ranges:
        - end: 599
          start: 300
      replicas: 1
      schedulingPolicy:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: topology.kubernetes.io/zone
                  operator: In
                  values:
                  - cn-guangzhou-b
    - env:
      - name: POOL_ID
        value: "1704821"
      - name: POOL_NAME
        value: "6182"
      name: "6182"
      ordinals:
        ranges:
        - end: 899
          start: 600
      replicas: 2
      schedulingPolicy:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: topology.kubernetes.io/zone
                  operator: In
                  values:
                  - cn-guangzhou-c
    name: mysql
    persistentVolumeClaimRetentionPolicy:
      whenDeleted: Delete
      whenScaled: Retain
    replicas: 5
    resources:
      limits:
        cpu: "2"
        memory: 4G
      requests:
        cpu: "2"
        memory: 4G
    serviceVersion: 5.7.44-vip.1
    volumeClaimTemplates:
    - name: data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 10G
        storageClassName: open-local-lvm
  terminationPolicy: Delete
  topology: replset
```
3. Applying a VerticalScaling Ops likes below
```
apiVersion: operations.kubeblocks.io/v1alpha1
kind: OpsRequest
metadata:
  name: t-vip-mysql57-xaza-vertical-scaling-9-8c9k9
  namespace: thomas-demo
spec:
  clusterName: t-vip-mysql57-xaza
  enqueueOnForce: false
  preConditionDeadlineSeconds: 0
  type: VerticalScaling
  verticalScaling:
  - componentName: mysql
    limits:
      cpu: "3"
      memory: 6G
    requests:
      cpu: "2"
      memory: 4G
```
5. Restarting all Pods, Cluster will became Running
```
> k get cluster -n thomas-demo -owide
NAME                 CLUSTER-DEFINITION   TERMINATION-POLICY   STATUS    AGE
t-vip-mysql57-xaza   vip-mysql            Delete               Running   2d15h
> k get pod -n thomas-demo -owide
NAME                                READY   STATUS    RESTARTS   AGE     IP              NODE             NOMINATED NODE   READINESS GATES
t-vip-mysql57-xaza-mysql-6182-600   2/2     Running   0          3m4s    10.189.95.41    10.189.110.107   <none>           <none>
t-vip-mysql57-xaza-mysql-6182-601   2/2     Running   0          2m11s   10.189.95.191   10.189.110.107   <none>           <none>
t-vip-mysql57-xaza-mysql-rim1-300   2/2     Running   0          107s    10.189.95.210   10.189.110.106   <none>           <none>
t-vip-mysql57-xaza-mysql-yua0-0     2/2     Running   0          71s     10.189.48.156   10.189.110.105   <none>           <none>
t-vip-mysql57-xaza-mysql-yua0-1     2/2     Running   0          56s     10.189.48.203   10.189.110.105   <none>           <none>
```
7. But VerticalScaling Ops still remains Running
```
k get ops -n thomas-demo -owide
NAME                                          TYPE              CLUSTER              STATUS    PROGRESS   AGE
t-vip-mysql57-xaza-vertical-scaling-9-8c9k9   VerticalScaling   t-vip-mysql57-xaza   Running   2/5        103m
```
**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - KB version: 1.0.0

**Additional context**
Add any other context about the problem here.


Hints from issue comments:
@lancelot1989: <img width="1746" height="936" alt="Image" src="https://github.com/user-attachments/assets/23c471b0-e165-4d07-a395-d8155c9c41bd" />

After debuging, it is confirmed that the problem is cause by using Ordinal Ranges.

Solution patch:
diff --git a/pkg/operations/vertical_scaling.go b/pkg/operations/vertical_scaling.go
index 1f2951211..c2bcbe96f 100644
--- a/pkg/operations/vertical_scaling.go
+++ b/pkg/operations/vertical_scaling.go
@@ -116,7 +116,11 @@ func (vs verticalScalingHandler) ReconcileAction(reqCtx intctrlutil.RequestCtx,
 				replicas := template.GetReplicas()
 				insVS := vsInsMap[template.Name]
 				if vs.verticalScalingInsTemplate(verticalScaling, template, insVS) {
-					templatePodNames, err := instanceset.GenerateInstanceNamesFromTemplate(workloadName, template.Name, replicas, pgRes.clusterComponent.OfflineInstances, nil)
+					ords, err := instanceset.ConvertOrdinalsToSortedList(template.Ordinals)
+					if err != nil {
+						return 0, 0, err
+					}
+					templatePodNames, err := instanceset.GenerateInstanceNamesFromTemplate(workloadName, template.Name, replicas, pgRes.clusterComponent.OfflineInstances, ords)
 					if err != nil {
 						return 0, 0, err
 					}



Test patch:
diff --git a/pkg/operations/vertical_scaling_test.go b/pkg/operations/vertical_scaling_test.go
index 67506a57c..5ad476342 100644
--- a/pkg/operations/vertical_scaling_test.go
+++ b/pkg/operations/vertical_scaling_test.go
@@ -20,6 +20,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package operations
 
 import (
+	"fmt"
 	"time"
 
 	. "github.com/onsi/ginkgo/v2"
@@ -66,6 +67,7 @@ var _ = Describe("VerticalScaling OpsRequest", func() {
 		ml := client.HasLabels{testCtx.TestObjLabelKey}
 		// namespaced
 		testapps.ClearResources(&testCtx, generics.OpsRequestSignature, inNS, ml)
+		testapps.ClearResources(&testCtx, generics.PodSignature, inNS, ml, client.GracePeriodSeconds(0))
 	}
 
 	BeforeEach(cleanEnv)
@@ -85,6 +87,20 @@ var _ = Describe("VerticalScaling OpsRequest", func() {
 			},
 		}
 
+		createPods := func(templateName string, ordinals ...int) []*corev1.Pod {
+			var pods []*corev1.Pod
+			prefix := ""
+			if templateName != "" {
+				prefix = "-" + templateName
+			}
+			for i := range ordinals {
+				podName := fmt.Sprintf("%s-%s%s-%d", clusterName, defaultCompName, prefix, ordinals[i])
+				pod := testapps.MockInstanceSetPod(&testCtx, nil, clusterName, defaultCompName, podName, "follower")
+				pods = append(pods, pod)
+			}
+			return pods
+		}
+
 		testVerticalScaling := func(verticalScaling []opsv1alpha1.VerticalScaling, instances []appsv1.InstanceTemplate) *OpsResource {
 			By("init operations resources ")
 			opsRes, _, _ := initOperationsResources(compDefName, clusterName)
@@ -145,6 +161,58 @@ var _ = Describe("VerticalScaling OpsRequest", func() {
 			Expect(opsRes.OpsRequest.Status.Progress).Should(Equal("0/3"))
 		})
 
+		It("vertical scaling the component which existing instance template with ordinal ranges", func() {
+			reqCtx := intctrlutil.RequestCtx{Ctx: ctx}
+			templateName := "foo-ranges"
+			verticalScaling := []opsv1alpha1.VerticalScaling{
+				{
+					ComponentOps:         opsv1alpha1.ComponentOps{ComponentName: defaultCompName},
+					ResourceRequirements: newResources,
+				},
+			}
+			opsRes := testVerticalScaling(verticalScaling, []appsv1.InstanceTemplate{{Name: templateName, Replicas: pointer.Int32(1), Ordinals: appsv1.Ordinals{Ranges: []appsv1.Range{{Start: 300, End: 600}}}}})
+			pods := make([]*corev1.Pod, 0)
+			pods = append(pods, createPods("", 0, 1)...)
+			pods = append(pods, createPods(templateName, 300)...)
+			By("mock ops running")
+			mockComponentIsOperating(opsRes.Cluster, appsv1.UpdatingComponentPhase, defaultCompName)
+			Expect(testapps.ChangeObjStatus(&testCtx, opsRes.OpsRequest, func() {
+				opsRes.OpsRequest.Status.Phase = opsv1alpha1.OpsRunningPhase
+				opsRes.OpsRequest.Status.StartTimestamp = metav1.Time{Time: time.Now()}
+			})).ShouldNot(HaveOccurred())
+			Expect(opsRes.OpsRequest.Status.Progress).Should(Equal("0/3"))
+			// wait 1 second for checking progress
+			time.Sleep(time.Second)
+			reCreatePod := func(pod *corev1.Pod) {
+				pod.Kind = constant.PodKind
+				testk8s.MockPodIsTerminating(ctx, testCtx, pod)
+				testk8s.RemovePodFinalizer(ctx, testCtx, pod)
+				testapps.MockInstanceSetPod(&testCtx, nil, clusterName, defaultCompName,
+					pod.Name, "leader", newResources)
+			}
+
+			By("restarting 1 pod")
+			reCreatePod(pods[0])
+
+			By("reconcile opsRequest status")
+			_, err := GetOpsManager().Reconcile(reqCtx, k8sClient, opsRes)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(opsRes.OpsRequest.Status.Progress).Should(Equal("1/3"))
+
+			By("restarting remain 2 pods")
+			reCreatePod(pods[1])
+			reCreatePod(pods[2])
+
+			By("mock cluster running")
+			mockComponentIsOperating(opsRes.Cluster, appsv1.RunningComponentPhase, defaultCompName)
+
+			By("reconcile opsRequest status")
+			_, err = GetOpsManager().Reconcile(reqCtx, k8sClient, opsRes)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(opsRes.OpsRequest.Status.Progress).Should(Equal("3/3"))
+			Expect(opsRes.OpsRequest.Status.Phase).Should(Equal(opsv1alpha1.OpsSucceedPhase))
+		})
+
 		It("vertical scaling the replicas which instance template is empty", func() {
 			templateName := "foo"
 			verticalScaling := []opsv1alpha1.VerticalScaling{