Repository: apecloud/kubeblocks

Issue:
[BUG] Do Not Append placement, as a multi-expr list for template
1. set placement info in cluster, for component and for template
![Image](https://github.com/user-attachments/assets/a7a3b373-984f-4832-bfcb-b3512613d635)

2. the pod in template will have placement merged, i.e. template = templage MERGE global(cluster)

![Image](https://github.com/user-attachments/assets/7f1c81ef-4156-44dc-b26b-3e74f479c449)

Multi-MatchExpr here will be OR-ed to place the POD.

## Expected Behavior
Placement should be AND-ed into ONE MatchExpr, those specified in Template first.

Hints from issue comments:

Solution patch:
diff --git a/apis/apps/v1/cluster_types.go b/apis/apps/v1/cluster_types.go
index ab4498c59..62d547302 100644
--- a/apis/apps/v1/cluster_types.go
+++ b/apis/apps/v1/cluster_types.go
@@ -342,6 +342,7 @@ type ClusterComponentSpec struct {
 	Replicas int32 `json:"replicas"`
 
 	// Specifies the scheduling policy for the Component.
+	// If defined, it will overwrite the scheduling policy defined in ClusterSpec.
 	//
 	// +optional
 	SchedulingPolicy *SchedulingPolicy `json:"schedulingPolicy,omitempty"`
diff --git a/apis/apps/v1/types.go b/apis/apps/v1/types.go
index d751a9301..bef3fdbeb 100644
--- a/apis/apps/v1/types.go
+++ b/apis/apps/v1/types.go
@@ -787,7 +787,8 @@ type InstanceTemplate struct {
 	// +optional
 	Labels map[string]string `json:"labels,omitempty"`
 
-	// Specifies the scheduling policy for the Component.
+	// Specifies the scheduling policy for the instance.
+	// If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
 	//
 	// +optional
 	SchedulingPolicy *SchedulingPolicy `json:"schedulingPolicy,omitempty"`
diff --git a/apis/apps/v1alpha1/component_conversion.go b/apis/apps/v1alpha1/component_conversion.go
index 942ad3ccc..930ff8fa1 100644
--- a/apis/apps/v1alpha1/component_conversion.go
+++ b/apis/apps/v1alpha1/component_conversion.go
@@ -88,6 +88,7 @@ func (r *Component) incrementConvertTo(dstRaw metav1.Object) (incrementChange, e
 			r.Annotations[clusterUIDKey] = clusterUID
 		}
 	}
+	comp.Spec.TerminationPolicy = appsv1.Delete
 	// deleted
 	return &componentConverter{
 		EnabledLogs:            r.Spec.EnabledLogs,
diff --git a/config/crd/bases/apps.kubeblocks.io_clusters.yaml b/config/crd/bases/apps.kubeblocks.io_clusters.yaml
index fdcdf44f2..43e17c25a 100644
--- a/config/crd/bases/apps.kubeblocks.io_clusters.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_clusters.yaml
@@ -1024,7 +1024,9 @@ spec:
                                 type: object
                             type: object
                           schedulingPolicy:
-                            description: Specifies the scheduling policy for the Component.
+                            description: |-
+                              Specifies the scheduling policy for the instance.
+                              If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                             properties:
                               affinity:
                                 description: Specifies a group of affinity scheduling
@@ -2412,7 +2414,9 @@ spec:
                       type: object
                       x-kubernetes-preserve-unknown-fields: true
                     schedulingPolicy:
-                      description: Specifies the scheduling policy for the Component.
+                      description: |-
+                        Specifies the scheduling policy for the Component.
+                        If defined, it will overwrite the scheduling policy defined in ClusterSpec.
                       properties:
                         affinity:
                           description: Specifies a group of affinity scheduling rules
@@ -8236,8 +8240,9 @@ spec:
                                     type: object
                                 type: object
                               schedulingPolicy:
-                                description: Specifies the scheduling policy for the
-                                  Component.
+                                description: |-
+                                  Specifies the scheduling policy for the instance.
+                                  If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                                 properties:
                                   affinity:
                                     description: Specifies a group of affinity scheduling
@@ -9643,7 +9648,9 @@ spec:
                           type: object
                           x-kubernetes-preserve-unknown-fields: true
                         schedulingPolicy:
-                          description: Specifies the scheduling policy for the Component.
+                          description: |-
+                            Specifies the scheduling policy for the Component.
+                            If defined, it will overwrite the scheduling policy defined in ClusterSpec.
                           properties:
                             affinity:
                               description: Specifies a group of affinity scheduling
diff --git a/config/crd/bases/apps.kubeblocks.io_components.yaml b/config/crd/bases/apps.kubeblocks.io_components.yaml
index b3afefaf6..c8a9b2653 100644
--- a/config/crd/bases/apps.kubeblocks.io_components.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_components.yaml
@@ -889,7 +889,9 @@ spec:
                           type: object
                       type: object
                     schedulingPolicy:
-                      description: Specifies the scheduling policy for the Component.
+                      description: |-
+                        Specifies the scheduling policy for the instance.
+                        If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                       properties:
                         affinity:
                           description: Specifies a group of affinity scheduling rules
diff --git a/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml b/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml
index 01c0bd11a..21f73c7d7 100644
--- a/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml
+++ b/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml
@@ -894,8 +894,9 @@ spec:
                                     type: object
                                 type: object
                               schedulingPolicy:
-                                description: Specifies the scheduling policy for the
-                                  Component.
+                                description: |-
+                                  Specifies the scheduling policy for the instance.
+                                  If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                                 properties:
                                   affinity:
                                     description: Specifies a group of affinity scheduling
@@ -3469,8 +3470,9 @@ spec:
                                     type: object
                                 type: object
                               schedulingPolicy:
-                                description: Specifies the scheduling policy for the
-                                  Component.
+                                description: |-
+                                  Specifies the scheduling policy for the instance.
+                                  If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                                 properties:
                                   affinity:
                                     description: Specifies a group of affinity scheduling
diff --git a/controllers/apps/component/transformer_component_service.go b/controllers/apps/component/transformer_component_service.go
index 44ccfb16d..ab48b388e 100644
--- a/controllers/apps/component/transformer_component_service.go
+++ b/controllers/apps/component/transformer_component_service.go
@@ -23,7 +23,6 @@ import (
 	"context"
 	"fmt"
 	"reflect"
-	"regexp"
 	"strconv"
 	"strings"
 
@@ -48,9 +47,6 @@ import (
 )
 
 var (
-	ordinalRegexpPattern = `-\d+$`
-	ordinalRegexp        = regexp.MustCompile(ordinalRegexpPattern)
-
 	multiClusterServicePlacementInMirror = "mirror"
 	multiClusterServicePlacementInUnique = "unique"
 )
@@ -134,48 +130,42 @@ func (t *componentServiceTransformer) isPodService(service *appsv1.ComponentServ
 
 func (t *componentServiceTransformer) buildPodService(comp *appsv1.Component,
 	synthesizeComp *component.SynthesizedComponent, service *appsv1.ComponentService) ([]*corev1.Service, error) {
-	pods, err := t.podsNameNOrdinal(synthesizeComp)
+	pods, err := t.podsNameNSuffix(synthesizeComp)
 	if err != nil {
 		return nil, err
 	}
 
 	services := make([]*appsv1.ComponentService, 0)
-	for name, ordinal := range pods {
+	for podName, suffix := range pods {
 		svc := service.DeepCopy()
-		svc.Name = fmt.Sprintf("%s-%d", service.Name, ordinal)
+		svc.Name = fmt.Sprintf("%s-%s", service.Name, suffix)
 		if len(service.ServiceName) == 0 {
-			svc.ServiceName = fmt.Sprintf("%d", ordinal)
+			svc.ServiceName = suffix
 		} else {
-			svc.ServiceName = fmt.Sprintf("%s-%d", service.ServiceName, ordinal)
+			svc.ServiceName = fmt.Sprintf("%s-%s", service.ServiceName, suffix)
 		}
 		if svc.Spec.Selector == nil {
 			svc.Spec.Selector = make(map[string]string)
 		}
-		svc.Spec.Selector[constant.KBAppPodNameLabelKey] = name
+		svc.Spec.Selector[constant.KBAppPodNameLabelKey] = podName
 		services = append(services, svc)
 	}
 	return t.buildServices(comp, synthesizeComp, services)
 }
 
-func (t *componentServiceTransformer) podsNameNOrdinal(synthesizeComp *component.SynthesizedComponent) (map[string]int, error) {
+func (t *componentServiceTransformer) podsNameNSuffix(synthesizeComp *component.SynthesizedComponent) (map[string]string, error) {
 	podNames, err := generatePodNames(synthesizeComp)
 	if err != nil {
 		return nil, err
 	}
-	pods := make(map[string]int)
-	for _, name := range podNames {
-		ordinal, err := func() (int, error) {
-			result := ordinalRegexp.FindString(name)
-			if len(result) == 0 {
-				return 0, fmt.Errorf("invalid pod name: %s", name)
-			}
-			o, _ := strconv.Atoi(result[1:])
-			return o, nil
-		}()
-		if err != nil {
-			return nil, err
+	pods := make(map[string]string)
+	prefix := fmt.Sprintf("%s-", synthesizeComp.FullCompName)
+	for _, podName := range podNames {
+		suffix, found := strings.CutPrefix(podName, prefix)
+		if !found || len(suffix) == 0 {
+			return nil, fmt.Errorf("invalid pod name when building pod services: %s", podName)
 		}
-		pods[name] = ordinal
+		pods[podName] = suffix
 	}
 	return pods, nil
 }
diff --git a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml
index fdcdf44f2..43e17c25a 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml
@@ -1024,7 +1024,9 @@ spec:
                                 type: object
                             type: object
                           schedulingPolicy:
-                            description: Specifies the scheduling policy for the Component.
+                            description: |-
+                              Specifies the scheduling policy for the instance.
+                              If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                             properties:
                               affinity:
                                 description: Specifies a group of affinity scheduling
@@ -2412,7 +2414,9 @@ spec:
                       type: object
                       x-kubernetes-preserve-unknown-fields: true
                     schedulingPolicy:
-                      description: Specifies the scheduling policy for the Component.
+                      description: |-
+                        Specifies the scheduling policy for the Component.
+                        If defined, it will overwrite the scheduling policy defined in ClusterSpec.
                       properties:
                         affinity:
                           description: Specifies a group of affinity scheduling rules
@@ -8236,8 +8240,9 @@ spec:
                                     type: object
                                 type: object
                               schedulingPolicy:
-                                description: Specifies the scheduling policy for the
-                                  Component.
+                                description: |-
+                                  Specifies the scheduling policy for the instance.
+                                  If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                                 properties:
                                   affinity:
                                     description: Specifies a group of affinity scheduling
@@ -9643,7 +9648,9 @@ spec:
                           type: object
                           x-kubernetes-preserve-unknown-fields: true
                         schedulingPolicy:
-                          description: Specifies the scheduling policy for the Component.
+                          description: |-
+                            Specifies the scheduling policy for the Component.
+                            If defined, it will overwrite the scheduling policy defined in ClusterSpec.
                           properties:
                             affinity:
                               description: Specifies a group of affinity scheduling
diff --git a/deploy/helm/crds/apps.kubeblocks.io_components.yaml b/deploy/helm/crds/apps.kubeblocks.io_components.yaml
index b3afefaf6..c8a9b2653 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_components.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_components.yaml
@@ -889,7 +889,9 @@ spec:
                           type: object
                       type: object
                     schedulingPolicy:
-                      description: Specifies the scheduling policy for the Component.
+                      description: |-
+                        Specifies the scheduling policy for the instance.
+                        If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                       properties:
                         affinity:
                           description: Specifies a group of affinity scheduling rules
diff --git a/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml b/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml
index 01c0bd11a..21f73c7d7 100755
--- a/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml
+++ b/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml
@@ -894,8 +894,9 @@ spec:
                                     type: object
                                 type: object
                               schedulingPolicy:
-                                description: Specifies the scheduling policy for the
-                                  Component.
+                                description: |-
+                                  Specifies the scheduling policy for the instance.
+                                  If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                                 properties:
                                   affinity:
                                     description: Specifies a group of affinity scheduling
@@ -3469,8 +3470,9 @@ spec:
                                     type: object
                                 type: object
                               schedulingPolicy:
-                                description: Specifies the scheduling policy for the
-                                  Component.
+                                description: |-
+                                  Specifies the scheduling policy for the instance.
+                                  If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.
                                 properties:
                                   affinity:
                                     description: Specifies a group of affinity scheduling
diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md
index c15d36d9c..249e4e1b6 100644
--- a/docs/developer_docs/api-reference/cluster.md
+++ b/docs/developer_docs/api-reference/cluster.md
@@ -2972,7 +2972,8 @@ SchedulingPolicy
 </td>
 <td>
 <em>(Optional)</em>
-<p>Specifies the scheduling policy for the Component.</p>
+<p>Specifies the scheduling policy for the Component.
+If defined, it will overwrite the scheduling policy defined in ClusterSpec.</p>
 </td>
 </tr>
 <tr>
@@ -7897,7 +7898,8 @@ SchedulingPolicy
 </td>
 <td>
 <em>(Optional)</em>
-<p>Specifies the scheduling policy for the Component.</p>
+<p>Specifies the scheduling policy for the instance.
+If defined, it will overwrite the scheduling policy defined in ClusterSpec and/or ClusterComponentSpec.</p>
 </td>
 </tr>
 <tr>
diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go
index 0c84cbb21..627c4b7b5 100644
--- a/pkg/controller/component/synthesize_component.go
+++ b/pkg/controller/component/synthesize_component.go
@@ -34,6 +34,7 @@ import (
 
 	appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
 	"github.com/apecloud/kubeblocks/pkg/constant"
+	"github.com/apecloud/kubeblocks/pkg/controller/scheduling"
 	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
 	"github.com/apecloud/kubeblocks/pkg/generics"
 	viper "github.com/apecloud/kubeblocks/pkg/viperx"
@@ -123,7 +124,7 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader,
 	}
 
 	// build scheduling policy for workload
-	buildSchedulingPolicy(synthesizeComp, comp)
+	scheduling.ApplySchedulingPolicyToPodSpec(synthesizeComp.PodSpec, comp.Spec.SchedulingPolicy)
 
 	buildFileTemplates(synthesizeComp, compDef, comp)
 	if err = overrideNCheckConfigTemplates(synthesizeComp, comp); err != nil {
@@ -250,18 +251,6 @@ func mergeUserDefinedEnv(synthesizedComp *SynthesizedComponent, comp *appsv1.Com
 	return nil
 }
 
-func buildSchedulingPolicy(synthesizedComp *SynthesizedComponent, comp *appsv1.Component) {
-	if comp.Spec.SchedulingPolicy != nil {
-		schedulingPolicy := comp.Spec.SchedulingPolicy
-		synthesizedComp.PodSpec.SchedulerName = schedulingPolicy.SchedulerName
-		synthesizedComp.PodSpec.NodeSelector = schedulingPolicy.NodeSelector
-		synthesizedComp.PodSpec.NodeName = schedulingPolicy.NodeName
-		synthesizedComp.PodSpec.Affinity = schedulingPolicy.Affinity
-		synthesizedComp.PodSpec.Tolerations = schedulingPolicy.Tolerations
-		synthesizedComp.PodSpec.TopologySpreadConstraints = schedulingPolicy.TopologySpreadConstraints
-	}
-}
-
 func buildVolumeClaimTemplates(synthesizeComp *SynthesizedComponent, comp *appsv1.Component) {
 	if comp.Spec.VolumeClaimTemplates != nil {
 		synthesizeComp.VolumeClaimTemplates = intctrlutil.ToCoreV1PVCTs(comp.Spec.VolumeClaimTemplates)
diff --git a/pkg/controller/component/workload_utils.go b/pkg/controller/component/workload_utils.go
index fcf51af5a..e1c3490fb 100644
--- a/pkg/controller/component/workload_utils.go
+++ b/pkg/controller/component/workload_utils.go
@@ -136,6 +136,7 @@ func GenerateAllPodNames(
 		templates = append(templates, &workloads.InstanceTemplate{
 			Name:     instances[i].Name,
 			Replicas: instances[i].Replicas,
+			Ordinals: instances[i].Ordinals,
 		})
 	}
 	return instanceset.GenerateAllInstanceNames(fullCompName, compReplicas, templates, offlineInstances, appsv1.Ordinals{})
diff --git a/pkg/controller/instanceset/instance_util.go b/pkg/controller/instanceset/instance_util.go
index efd36005b..9d9f580ad 100644
--- a/pkg/controller/instanceset/instance_util.go
+++ b/pkg/controller/instanceset/instance_util.go
@@ -413,6 +413,7 @@ func GenerateInstanceNamesWithOrdinalList(parentName, templateName string,
 	replicas int32, offlineInstances []string, ordinalList []int32) ([]string, error) {
 	var instanceNameList []string
 	usedNames := sets.New(offlineInstances...)
+	slices.Sort(ordinalList)
 	for _, ordinal := range ordinalList {
 		var name string
 		if len(templateName) == 0 {
@@ -424,6 +425,9 @@ func GenerateInstanceNamesWithOrdinalList(parentName, templateName string,
 			continue
 		}
 		instanceNameList = append(instanceNameList, name)
+		if len(instanceNameList) == int(replicas) {
+			break
+		}
 	}
 	if int32(len(instanceNameList)) != replicas {
 		errorMessage := fmt.Sprintf("for template '%s', expected %d instance names but generated %d: [%s]",
@@ -656,7 +660,7 @@ func copyAndMerge(oldObj, newObj client.Object) client.Object {
 		})
 		mergeMap(&newSvc.Annotations, &oldSvc.Annotations)
 		mergeMap(&newSvc.Labels, &oldSvc.Labels)
-		mergeMap(&newSvc.Spec.Selector, &oldSvc.Spec.Selector)
+		oldSvc.Spec.Selector = newSvc.Spec.Selector
 		oldSvc.Spec.Type = newSvc.Spec.Type
 		oldSvc.Spec.PublishNotReadyAddresses = newSvc.Spec.PublishNotReadyAddresses
 		// ignore NodePort&LB svc here, instanceSet only supports default headless svc
@@ -923,28 +927,7 @@ func buildInstanceTemplateExt(template workloads.InstanceTemplate, templateExt *
 		}
 	}
 
-	if sp := template.SchedulingPolicy; sp != nil {
-		if sp.SchedulerName != "" {
-			templateExt.Spec.SchedulerName = sp.SchedulerName
-		}
-		mergeMap(&sp.NodeSelector, &templateExt.Spec.NodeSelector)
-		if sp.NodeName != "" {
-			templateExt.Spec.NodeName = sp.NodeName
-		}
-		templateExt.Spec.Affinity = scheduling.MergeAffinity(sp.Affinity, templateExt.Spec.Affinity)
-		intctrlutil.MergeList(&sp.Tolerations, &templateExt.Spec.Tolerations,
-			func(item corev1.Toleration) func(corev1.Toleration) bool {
-				return func(t corev1.Toleration) bool {
-					return reflect.DeepEqual(item, t)
-				}
-			})
-		intctrlutil.MergeList(&sp.TopologySpreadConstraints, &templateExt.Spec.TopologySpreadConstraints,
-			func(item corev1.TopologySpreadConstraint) func(corev1.TopologySpreadConstraint) bool {
-				return func(t corev1.TopologySpreadConstraint) bool {
-					return reflect.DeepEqual(item, t)
-				}
-			})
-	}
+	scheduling.ApplySchedulingPolicyToPodSpec(&templateExt.Spec, template.SchedulingPolicy)
 }
 
 func mergeCPUNMemory(s, d *corev1.ResourceList) {
diff --git a/pkg/controller/scheduling/scheduling_utils.go b/pkg/controller/scheduling/scheduling_utils.go
index 39b4e236c..bc4dd9dca 100644
--- a/pkg/controller/scheduling/scheduling_utils.go
+++ b/pkg/controller/scheduling/scheduling_utils.go
@@ -31,6 +31,18 @@ import (
 	viper "github.com/apecloud/kubeblocks/pkg/viperx"
 )
 
+// ApplySchedulingPolicyToPodSpec overrides podSpec with schedulingPolicy, if schedulingPolicy is not nil
+func ApplySchedulingPolicyToPodSpec(podSpec *corev1.PodSpec, schedulingPolicy *appsv1.SchedulingPolicy) {
+	if schedulingPolicy != nil {
+		podSpec.SchedulerName = schedulingPolicy.SchedulerName
+		podSpec.NodeSelector = schedulingPolicy.NodeSelector
+		podSpec.NodeName = schedulingPolicy.NodeName
+		podSpec.Affinity = schedulingPolicy.Affinity
+		podSpec.Tolerations = schedulingPolicy.Tolerations
+		podSpec.TopologySpreadConstraints = schedulingPolicy.TopologySpreadConstraints
+	}
+}
+
 func BuildSchedulingPolicy(cluster *appsv1.Cluster, compSpec *appsv1.ClusterComponentSpec) (*appsv1.SchedulingPolicy, error) {
 	if cluster.Spec.SchedulingPolicy != nil || (compSpec != nil && compSpec.SchedulingPolicy != nil) {
 		return buildSchedulingPolicy(cluster, compSpec)
@@ -142,6 +154,7 @@ func MergeAffinity(src, dst *corev1.Affinity) *corev1.Affinity {
 			if rtn.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
 				rtn.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{}
 			}
+			// FIXME: NodeSelectorTerms are ORed, this can be a problem
 			intctrlutil.MergeList(&src.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, &rtn.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, makeCmp[corev1.NodeSelectorTerm]())
 		}
 		intctrlutil.MergeList(&src.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution, &rtn.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution, makeCmp[corev1.PreferredSchedulingTerm]())



Test patch:
diff --git a/pkg/controller/instanceset/instance_util_test.go b/pkg/controller/instanceset/instance_util_test.go
index b6b2faf22..d4034d147 100644
--- a/pkg/controller/instanceset/instance_util_test.go
+++ b/pkg/controller/instanceset/instance_util_test.go
@@ -649,6 +649,44 @@ var _ = Describe("instance util test", func() {
 		})
 	})
 
+	It("with templatesOrdinals range", func() {
+		By("replicas is equal to the length of ordinals ranges")
+		parentName := "test"
+		templateFoo := &workloads.InstanceTemplate{
+			Name:     "foo",
+			Replicas: pointer.Int32(3),
+			Ordinals: kbappsv1.Ordinals{
+				Ranges: []kbappsv1.Range{
+					{
+						Start: 1,
+						End:   2,
+					},
+					{
+						Start: 5,
+						End:   5,
+					},
+				},
+			},
+		}
+		instanceNameList, err := GenerateAllInstanceNames(parentName, 3, []InstanceTemplate{templateFoo}, nil, kbappsv1.Ordinals{})
+		Expect(err).Should(BeNil())
+		Expect(len(instanceNameList)).Should(Equal(3))
+
+		By("replicas is less than the length of ordinals ranges")
+		templateFoo.Replicas = pointer.Int32(2)
+		instanceNameList, err = GenerateAllInstanceNames(parentName, 2, []InstanceTemplate{templateFoo}, nil, kbappsv1.Ordinals{})
+		Expect(err).Should(BeNil())
+		Expect(len(instanceNameList)).Should(Equal(2))
+
+		By("replicas is greater than the length of ordinals ranges")
+		templateFoo.Replicas = pointer.Int32(4)
+		_, err = GenerateAllInstanceNames(parentName, 4, []InstanceTemplate{templateFoo}, nil, kbappsv1.Ordinals{})
+		errInstanceNameListExpected := []string{"test-foo-1", "test-foo-2", "test-foo-5"}
+		errExpected := fmt.Errorf("for template '%s', expected %d instance names but generated %d: [%s]",
+			templateFoo.Name, *templateFoo.Replicas, len(errInstanceNameListExpected), strings.Join(errInstanceNameListExpected, ", "))
+		Expect(err).Should(Equal(errExpected))
+	})
+
 	Context("GetOrdinalListByTemplateName", func() {
 		It("should work well", func() {
 			its := &workloads.InstanceSet{
diff --git a/pkg/controller/scheduling/scheduling_utils_test.go b/pkg/controller/scheduling/scheduling_utils_test.go
index 9bf566ac9..e2f0a1ca0 100644
--- a/pkg/controller/scheduling/scheduling_utils_test.go
+++ b/pkg/controller/scheduling/scheduling_utils_test.go
@@ -24,6 +24,8 @@ import (
 	. "github.com/onsi/gomega"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
 )
 
 var _ = Describe("Scheduling util test", func() {
@@ -570,4 +572,26 @@ var _ = Describe("Scheduling util test", func() {
 			Expect(rtn).Should(Equal(expectMergedAffinity))
 		})
 	})
+
+	Context("ApplySchedulingPolicyToPodSpec", func() {
+		It("works", func() {
+			pod := &corev1.Pod{
+				Spec: corev1.PodSpec{
+					NodeSelector: map[string]string{
+						"foo": "bar",
+					},
+				},
+			}
+
+			ApplySchedulingPolicyToPodSpec(&pod.Spec, nil)
+			Expect(pod.Spec.NodeSelector["foo"]).Should(Equal("bar"))
+
+			ApplySchedulingPolicyToPodSpec(&pod.Spec, &appsv1.SchedulingPolicy{
+				NodeSelector: map[string]string{
+					"foo": "baz",
+				},
+			})
+			Expect(pod.Spec.NodeSelector["foo"]).Should(Equal("baz"))
+		})
+	})
 })