Repository: pingcap/tidb-operator
Pull request #6202: feat(tidb): support setting server labels dynamically
Author: login: fgksgf
Labels: name: lgtm
name: approved
name: size/L
name: v2
description: for operator v2
Created: 2025-05-19T04:05:57Z | Merged: 2025-05-21T09:43:10Z

<!--
Thank you for contributing to TiDB Operator!
Please complete the following template before creating a PR.
Ref: TiDB Operator [CONTRIBUTING](https://github.com/pingcap/tidb-operator/blob/master/CONTRIBUTING.md) document
-->

### What problem does this PR solve?
<!--
Please describe the problem AS DETAILED AS POSSIBLE.
Add an ISSUE LINK WITH SUMMARY if exists.

For example:

    Fix the bug that syncing `Backup` CR will crash tidb-controller-manager pod when client TLS feature is enabled.

    Closes #xxx (issue number)
-->

### What is changed and how does it work?
<!--
Please describe the design that your implementation follows AS DETAILED AS POSSIBLE.

For example:

    The root cause is a nil pointer dereferencing. Add a `ptr != nil` check before access members of `ptr` to prevent the crash.
-->

### Code changes

- [x] Has Go code change
- [ ] Has CI related scripts change

### Tests
<!-- AT LEAST ONE test must be included. -->

- [x] Unit test <!-- If you added any unit test cases, check this box -->
- [ ] E2E test <!-- If you added any e2e test cases, check this box -->
- [ ] Manual test <!-- If this PR needs manual test, check this box, and add detailed manual test scripts or steps below, so that ANYONE CAN REPRODUCE IT. Ref: https://github.com/pingcap/tidb-operator/pull/3517 -->
- [ ] No code <!-- If this PR contains no code changes, check this box -->

### Side effects

- [ ] Breaking backward compatibility <!-- If this PR breaks things deployed with previous TiDB Operator versions, check this box -->
- [ ] Other side effects: <!-- Any other side effects, such as requiring additional storage / consumes substantial memory / potential reconciliation latency -->

### Related changes

- [ ] Need to cherry-pick to the release branch <!-- If this PR should also appear in the current release branch, check this box -->
- [ ] Need to update the documentation <!-- If this PR introduces new features or changes previous usages, check this box -->

### Release Notes
<!--
If no need to add a release note, just type `NONE` in the following `release-note` block.
If the PR requires additional action from users to deploy the new release, start the release note with "ACTION REQUIRED: ".
-->
Please refer to [Release Notes Language Style Guide](https://github.com/pingcap/tidb-operator/blob/master/docs/release-note-guide.md) before writing the release note.

```release-note

```



Diff:
diff --git a/.github/licenserc.yaml b/.github/licenserc.yaml
index 12d7b695610..881026311f8 100644
--- a/.github/licenserc.yaml
+++ b/.github/licenserc.yaml
@@ -28,4 +28,5 @@ header:
     - tests/e2e/example/data
     - '**/OWNERS'
     - OWNERS_ALIASES
+    - .cursorignore
   comment: on-failure
diff --git a/api/core/v1alpha1/tidb_types.go b/api/core/v1alpha1/tidb_types.go
index 6d4c19feb73..1a4441a2bd0 100644
--- a/api/core/v1alpha1/tidb_types.go
+++ b/api/core/v1alpha1/tidb_types.go
@@ -191,6 +191,15 @@ type TiDBSecurity struct {
 type TiDBServer struct {
 	// Port defines all ports listened by TiDB.
 	Ports TiDBPorts `json:"ports,omitempty"`
+
+	// Labels defines the server labels of the TiDB server.
+	// TiDB Operator will ignore `labels` in TiDB's config file and use this field instead.
+	// Note these label keys are managed by TiDB Operator, it will be set automatically and you can not modify them:
+	//  - host
+	//  - region
+	//  - zone
+	// +kubebuilder:validation:XValidation:rule="!('host' in self) && !('region' in self) && !('zone' in self)",message="labels cannot contain 'host', 'region', or 'zone' keys"
+	Labels map[string]string `json:"labels,omitempty"`
 }
 
 type TiDBPorts struct {
diff --git a/api/core/v1alpha1/zz_generated.deepcopy.go b/api/core/v1alpha1/zz_generated.deepcopy.go
index 7d467e4ba52..2ebca49f50d 100644
--- a/api/core/v1alpha1/zz_generated.deepcopy.go
+++ b/api/core/v1alpha1/zz_generated.deepcopy.go
@@ -2087,6 +2087,13 @@ func (in *TiDBSecurity) DeepCopy() *TiDBSecurity {
 func (in *TiDBServer) DeepCopyInto(out *TiDBServer) {
 	*out = *in
 	in.Ports.DeepCopyInto(&out.Ports)
+	if in.Labels != nil {
+		in, out := &in.Labels, &out.Labels
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
 	return
 }
 
diff --git a/manifests/crd/core.pingcap.com_tidbgroups.yaml b/manifests/crd/core.pingcap.com_tidbgroups.yaml
index 3cd3a55a2b2..4ea563f8a2a 100644
--- a/manifests/crd/core.pingcap.com_tidbgroups.yaml
+++ b/manifests/crd/core.pingcap.com_tidbgroups.yaml
@@ -8593,6 +8593,22 @@ spec:
                       server:
                         description: Server defines the server configuration of TiDB.
                         properties:
+                          labels:
+                            additionalProperties:
+                              type: string
+                            description: |-
+                              Labels defines the server labels of the TiDB server.
+                              TiDB Operator will ignore `labels` in TiDB's config file and use this field instead.
+                              Note these label keys are managed by TiDB Operator, it will be set automatically and you can not modify them:
+                               - host
+                               - region
+                               - zone
+                            type: object
+                            x-kubernetes-validations:
+                            - message: labels cannot contain 'host', 'region', or
+                                'zone' keys
+                              rule: '!(''host'' in self) && !(''region'' in self)
+                                && !(''zone'' in self)'
                           ports:
                             description: Port defines all ports listened by TiDB.
                             properties:
diff --git a/manifests/crd/core.pingcap.com_tidbs.yaml b/manifests/crd/core.pingcap.com_tidbs.yaml
index 9b13e2b6e0f..19245c155fc 100644
--- a/manifests/crd/core.pingcap.com_tidbs.yaml
+++ b/manifests/crd/core.pingcap.com_tidbs.yaml
@@ -8280,6 +8280,21 @@ spec:
               server:
                 description: Server defines the server configuration of TiDB.
                 properties:
+                  labels:
+                    additionalProperties:
+                      type: string
+                    description: |-
+                      Labels defines the server labels of the TiDB server.
+                      TiDB Operator will ignore `labels` in TiDB's config file and use this field instead.
+                      Note these label keys are managed by TiDB Operator, it will be set automatically and you can not modify them:
+                       - host
+                       - region
+                       - zone
+                    type: object
+                    x-kubernetes-validations:
+                    - message: labels cannot contain 'host', 'region', or 'zone' keys
+                      rule: '!(''host'' in self) && !(''region'' in self) && !(''zone''
+                        in self)'
                   ports:
                     description: Port defines all ports listened by TiDB.
                     properties:
diff --git a/pkg/configs/tidb/config.go b/pkg/configs/tidb/config.go
index 35e39c48c19..86e888590ae 100644
--- a/pkg/configs/tidb/config.go
+++ b/pkg/configs/tidb/config.go
@@ -39,8 +39,9 @@ type Config struct {
 
 	Log Log `toml:"log"`
 
-	InitializeSQLFile          string `toml:"initialize-sql-file"`
-	GracefulWaitBeforeShutdown int    `toml:"graceful-wait-before-shutdown"`
+	InitializeSQLFile          string            `toml:"initialize-sql-file"`
+	GracefulWaitBeforeShutdown int               `toml:"graceful-wait-before-shutdown"`
+	ServerLabels               map[string]string `toml:"labels"`
 }
 
 type Security struct {
@@ -145,6 +146,10 @@ func (c *Config) Validate(separateSlowLog bool) error {
 		fields = append(fields, "initialize-sql-file")
 	}
 
+	if len(c.ServerLabels) > 0 {
+		fields = append(fields, "labels")
+	}
+
 	if len(fields) == 0 {
 		return nil
 	}
diff --git a/pkg/configs/tidb/config_test.go b/pkg/configs/tidb/config_test.go
index c0d9c6f6c03..90d0ba7d04c 100644
--- a/pkg/configs/tidb/config_test.go
+++ b/pkg/configs/tidb/config_test.go
@@ -48,6 +48,7 @@ func TestValidate(t *testing.T) {
 			SlowQueryFile: "/path/to/slow-query-file",
 		},
 		InitializeSQLFile: "/path/to/initialize-sql-file",
+		ServerLabels:      map[string]string{"foo": "bar"},
 	}
 
 	err = cfgInvalid.Validate(true)
@@ -65,6 +66,7 @@ func TestValidate(t *testing.T) {
 	assert.Contains(t, err.Error(), "security.auth-token-jwks")
 	assert.Contains(t, err.Error(), "log.slow-query-file")
 	assert.Contains(t, err.Error(), "initialize-sql-file")
+	assert.Contains(t, err.Error(), "labels")
 }
 
 func TestOverlay(t *testing.T) {
diff --git a/pkg/controllers/tidb/tasks/server_labels.go b/pkg/controllers/tidb/tasks/server_labels.go
index 9b53fac9c2f..f0e73d5d3ef 100644
--- a/pkg/controllers/tidb/tasks/server_labels.go
+++ b/pkg/controllers/tidb/tasks/server_labels.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/pingcap/tidb-operator/pkg/client"
 	"github.com/pingcap/tidb-operator/pkg/utils/k8s"
+	maputil "github.com/pingcap/tidb-operator/pkg/utils/map"
 	"github.com/pingcap/tidb-operator/pkg/utils/task/v3"
 )
 
@@ -70,9 +71,9 @@ func TaskServerLabels(state *ReconcileContext, c client.Client) task.Task {
 			return task.Complete().With("zone labels not found in pd location-label, skip sync server labels")
 		}
 
-		serverLabels := k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels)
+		serverLabels := maputil.Merge(state.TiDB().Spec.Server.Labels, k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels))
 		if len(serverLabels) == 0 {
-			return task.Complete().With("no server labels from node %s to sync", nodeName)
+			return task.Complete().With("no server labels to sync")
 		}
 		serverLabels[tidbDCLabel] = serverLabels[zoneLabel]
 
diff --git a/pkg/reloadable/tidb.go b/pkg/reloadable/tidb.go
index e16408b8b53..d05f920bd08 100644
--- a/pkg/reloadable/tidb.go
+++ b/pkg/reloadable/tidb.go
@@ -97,6 +97,9 @@ func convertTiDBTemplate(tmpl *v1alpha1.TiDBTemplate) *v1alpha1.TiDBTemplate {
 		newTmpl.Spec.SlowLog.Image = nil
 	}
 
+	// server labels can be updated dynamically
+	newTmpl.Spec.Server.Labels = map[string]string{}
+
 	newTmpl.Spec.Volumes = convertVolumes(newTmpl.Spec.Volumes)
 	newTmpl.Spec.Overlay = convertOverlay(newTmpl.Spec.Overlay)
 
diff --git a/pkg/reloadable/tidb_test.go b/pkg/reloadable/tidb_test.go
index 965ed2229fd..cf97b587dd4 100644
--- a/pkg/reloadable/tidb_test.go
+++ b/pkg/reloadable/tidb_test.go
@@ -103,6 +103,26 @@ func TestCheckTiDB(t *testing.T) {
 			},
 			reloadable: false,
 		},
+		{
+			desc: "update the server labels",
+			dbg: &v1alpha1.TiDBGroup{
+				Spec: v1alpha1.TiDBGroupSpec{
+					Template: v1alpha1.TiDBTemplate{
+						Spec: v1alpha1.TiDBTemplateSpec{
+							Server: v1alpha1.TiDBServer{
+								Labels: map[string]string{"foo": "bar"},
+							},
+						},
+					},
+				},
+			},
+			db: &v1alpha1.TiDB{
+				Spec: v1alpha1.TiDBSpec{
+					TiDBTemplateSpec: v1alpha1.TiDBTemplateSpec{},
+				},
+			},
+			reloadable: true,
+		},
 	}
 
 	for i := range cases {
diff --git a/pkg/utils/k8s/node.go b/pkg/utils/k8s/node.go
index 4fcc90e470f..30c8d23c56f 100644
--- a/pkg/utils/k8s/node.go
+++ b/pkg/utils/k8s/node.go
@@ -28,7 +28,7 @@ var shortLabelNameToK8sLabel = map[string][]string{
 }
 
 // GetNodeLabelsForKeys gets the labels of the node for the specified keys.
-// This function is used to get the lables for setting store & server labels.
+// This function is used to get the labels for setting store & server labels.
 func GetNodeLabelsForKeys(node *corev1.Node, keys []string) map[string]string {
 	labels := make(map[string]string)
 	for _, key := range keys {
diff --git a/tests/validation/tidb_test.go b/tests/validation/tidb_test.go
new file mode 100644
index 00000000000..7497579d160
--- /dev/null
+++ b/tests/validation/tidb_test.go
@@ -0,0 +1,121 @@
+// Copyright 2024 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package validation
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/util/yaml"
+)
+
+func TestTiDB(t *testing.T) {
+	cases := []Case{}
+	cases = append(cases, transferTiDBCases(t, Topology(), "spec", "topology")...)
+	cases = append(cases, transferTiDBCases(t, ClusterReference(), "spec", "cluster")...)
+	cases = append(cases, transferTiDBCases(t, serverLabels(), "spec", "server", "labels")...)
+
+	Validate(t, "crd/core.pingcap.com_tidbs.yaml", cases)
+}
+
+func basicTiDB() map[string]any {
+	data := []byte(`
+apiVersion: core.pingcap.com/v1alpha1
+kind: TiDB
+metadata:
+  name: tidb
+spec:
+  cluster:
+    name: test
+  subdomain: test
+  version: v8.1.0
+`)
+	obj := map[string]any{}
+	if err := yaml.Unmarshal(data, &obj); err != nil {
+		panic(err)
+	}
+
+	return obj
+}
+
+func transferTiDBCases(t *testing.T, cases []Case, fields ...string) []Case {
+	for i := range cases {
+		c := &cases[i]
+
+		current := basicTiDB()
+		if c.current == nil {
+			unstructured.RemoveNestedField(current, fields...)
+		} else {
+			require.NoError(t, unstructured.SetNestedField(current, c.current, fields...))
+		}
+
+		c.current = current
+
+		if c.isCreate {
+			c.old = nil
+			continue
+		}
+
+		old := basicTiDB()
+		if c.old == nil {
+			unstructured.RemoveNestedField(old, fields...)
+		} else {
+			require.NoError(t, unstructured.SetNestedField(old, c.old, fields...))
+		}
+
+		c.old = old
+	}
+
+	return cases
+}
+
+func serverLabels() []Case {
+	errMsg := "spec.server.labels: Invalid value: \"object\": labels cannot contain 'host', 'region', or 'zone' keys"
+
+	return []Case{
+		{
+			desc:     "set host label",
+			isCreate: true,
+			current: map[string]any{
+				"host": "foo",
+			},
+			wantErrs: []string{errMsg},
+		},
+		{
+			desc:     "set region label",
+			isCreate: true,
+			current: map[string]any{
+				"region": "foo",
+			},
+			wantErrs: []string{errMsg},
+		},
+		{
+			desc:     "set zone label",
+			isCreate: true,
+			current: map[string]any{
+				"zone": "foo",
+			},
+			wantErrs: []string{errMsg},
+		},
+		{
+			desc:     "set custom label",
+			isCreate: true,
+			current: map[string]any{
+				"foo": "bar",
+			},
+		},
+	}
+}


Reviews:
user: login: liubog2008
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T09:09:50Z
user: login: fgksgf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T10:45:44Z
user: login: liubog2008
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T06:39:24Z
user: login: liubog2008
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T06:42:06Z
user: login: liubog2008
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T06:57:56Z
user: login: liubog2008
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T07:08:14Z
user: login: fgksgf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T07:27:27Z

Review comments:
diff_hunk: @@ -190,6 +190,13 @@ type TiDBSecurity struct {
 type TiDBServer struct {
 	// Port defines all ports listened by TiDB.
 	Ports TiDBPorts `json:"ports,omitempty"`
+
+	// Labels defines the server labels of the TiDB server.
+	// TiDB Operator will ignore `labels` in TiDB's config file and use this field instead.
+	// Note these label keys are managed by TiDB Operator, it will be set automatically and you can not modify them:
+	//  - zone
+	//  - topology.kubernetes.io/zone
+	Labels map[string]string `json:"labels,omitempty"`
path: api/core/v1alpha1/tidb_types.go
user: login: liubog2008
body: add an validation rule to verify that no `host`, `region`, `zone` keys in Labels.
created_at: 2025-05-20T09:09:50Z
author_association: MEMBER
diff_hunk: @@ -190,6 +190,13 @@ type TiDBSecurity struct {
 type TiDBServer struct {
 	// Port defines all ports listened by TiDB.
 	Ports TiDBPorts `json:"ports,omitempty"`
+
+	// Labels defines the server labels of the TiDB server.
+	// TiDB Operator will ignore `labels` in TiDB's config file and use this field instead.
+	// Note these label keys are managed by TiDB Operator, it will be set automatically and you can not modify them:
+	//  - zone
+	//  - topology.kubernetes.io/zone
path: api/core/v1alpha1/tidb_types.go
user: login: fgksgf
body: ```suggestion
	// TiDB Operator will ignore `labels` in TiDB's config file and use this field instead.
	// Note these label keys are managed by TiDB Operator, it will be set automatically and you can not modify them:
	//  - zone
	//  - host
	//  - region
	// +kubebuilder:validation:XValidation:rule="!has(self.host) && !has(self.region) && !has(self.zone)",message="labels cannot contain 'host', 'region', or 'zone' keys"
```
created_at: 2025-05-20T10:45:44Z
author_association: MEMBER
diff_hunk: @@ -97,6 +97,9 @@ func convertTiDBTemplate(tmpl *v1alpha1.TiDBTemplate) *v1alpha1.TiDBTemplate {
 		newTmpl.Spec.SlowLog.Image = nil
 	}
 
+	// server labels can be updated dynamically
+	newTmpl.Spec.Server.Labels = map[string]string{}
path: pkg/reloadable/tidb.go
user: login: liubog2008
body: reset to nil?
created_at: 2025-05-21T06:39:24Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,121 @@
+// Copyright 2024 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package validation
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/util/yaml"
+)
+
+func TestTiDB(t *testing.T) {
+	cases := []Case{}
+	cases = append(cases, transferTiDBCases(t, Topology(), "spec", "topology")...)
+	cases = append(cases, transferTiDBCases(t, ClusterReference(), "spec", "cluster")...)
+	cases = append(cases, transferTiDBCases(t, serverLabels(), "spec", "server", "labels")...)
+
+	Validate(t, "crd/core.pingcap.com_tidbs.yaml", cases)
+}
+
+func basicTiDB() map[string]any {
+	data := []byte(`
+apiVersion: core.pingcap.com/v1alpha1
+kind: TiDB
+metadata:
+  name: tidb
+spec:
+  cluster:
+    name: test
+  subdomain: test
+  version: v8.1.0
+`)
+	obj := map[string]any{}
+	if err := yaml.Unmarshal(data, &obj); err != nil {
+		panic(err)
+	}
+
+	return obj
+}
+
+func transferTiDBCases(t *testing.T, cases []Case, fields ...string) []Case {
+	for i := range cases {
+		c := &cases[i]
+
+		current := basicTiDB()
+		if c.current == nil {
+			unstructured.RemoveNestedField(current, fields...)
+		} else {
+			require.NoError(t, unstructured.SetNestedField(current, c.current, fields...))
+		}
+
+		c.current = current
+
+		if c.isCreate {
+			c.old = nil
+			continue
+		}
+
+		old := basicTiDB()
+		if c.old == nil {
+			unstructured.RemoveNestedField(old, fields...)
+		} else {
+			require.NoError(t, unstructured.SetNestedField(old, c.old, fields...))
+		}
+
+		c.old = old
+	}
+
+	return cases
+}
+
+func serverLabels() []Case {
+	errMsg := "spec.server.labels: Invalid value: \"object\": labels cannot contain 'host', 'region', or 'zone' keys"
path: tests/validation/tidb_test.go
user: login: liubog2008
body: also add `topology.kubernetes.io/zone`, `topology.kubernetes.io/region` and `kubernetes.io/hostname` ? 
created_at: 2025-05-21T06:42:06Z
author_association: MEMBER
diff_hunk: @@ -70,9 +71,9 @@ func TaskServerLabels(state *ReconcileContext, c client.Client) task.Task {
 			return task.Complete().With("zone labels not found in pd location-label, skip sync server labels")
 		}
 
-		serverLabels := k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels)
+		serverLabels := maputil.Merge(state.TiDB().Spec.Server.Labels, k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels))
path: pkg/controllers/tidb/tasks/server_labels.go
user: login: liubog2008
body: How about always adding `region`, `zone` and `host` into `LocationLabels`?
created_at: 2025-05-21T06:57:56Z
author_association: MEMBER
diff_hunk: @@ -70,9 +71,9 @@ func TaskServerLabels(state *ReconcileContext, c client.Client) task.Task {
 			return task.Complete().With("zone labels not found in pd location-label, skip sync server labels")
 		}
 
-		serverLabels := k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels)
+		serverLabels := maputil.Merge(state.TiDB().Spec.Server.Labels, k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels))
path: pkg/controllers/tidb/tasks/server_labels.go
user: login: liubog2008
body: Open a new PR if it's needed
created_at: 2025-05-21T07:08:14Z
author_association: MEMBER
diff_hunk: @@ -70,9 +71,9 @@ func TaskServerLabels(state *ReconcileContext, c client.Client) task.Task {
 			return task.Complete().With("zone labels not found in pd location-label, skip sync server labels")
 		}
 
-		serverLabels := k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels)
+		serverLabels := maputil.Merge(state.TiDB().Spec.Server.Labels, k8s.GetNodeLabelsForKeys(&node, pdCfg.Replication.LocationLabels))
path: pkg/controllers/tidb/tasks/server_labels.go
user: login: fgksgf
body: Don't know if there will be side effects, had better align with v1.
created_at: 2025-05-21T07:27:27Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-19T04:06:11Z
body: /run-pull-e2e-kind-v2
author_association: NONE
user: login: codecov-commenter
created_at: 2025-05-19T04:10:39Z
body: ## [Codecov](https://app.codecov.io/gh/pingcap/tidb-operator/pull/6202?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pingcap) Report
Attention: Patch coverage is `71.42857%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 46.87%. Comparing base [(`a0adff1`)](https://app.codecov.io/gh/pingcap/tidb-operator/commit/a0adff1b664f798a223a8af5087ca3dff611e8e8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pingcap) to head [(`22d6c85`)](https://app.codecov.io/gh/pingcap/tidb-operator/commit/22d6c8560d4dcbf1fc39ce6350141d681190151d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pingcap).

<details><summary>Additional details and impacted files</summary>


```diff
@@              Coverage Diff               @@
##           feature/v2    #6202      +/-   ##
==============================================
+ Coverage       46.85%   46.87%   +0.01%     
==============================================
  Files             258      258              
  Lines           17476    17481       +5     
==============================================
+ Hits             8189     8194       +5     
  Misses           9287     9287              
```

| [Flag](https://app.codecov.io/gh/pingcap/tidb-operator/pull/6202/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pingcap) | Coverage Δ | |
|---|---|---|
| [unittest](https://app.codecov.io/gh/pingcap/tidb-operator/pull/6202/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pingcap) | `46.87% <71.42%> (+0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pingcap#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-21T02:26:12Z
body: /run-pull-e2e-kind-v2
author_association: NONE
user: login: liubog2008
created_at: 2025-05-21T07:17:53Z
body: /lgtm
author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-05-21T07:18:02Z
body: /run-pull-e2e-kind-v2
author_association: NONE
user: login: ti-chi-bot[bot]
created_at: 2025-05-21T07:22:49Z
body: @fgksgf: Your PR was out of date, I have automatically updated it for you.


<details>

Instructions for interacting with me using PR comments are available [here](https://prow.tidb.net/command-help).  If you have questions or suggestions related to my behavior, please file an issue against the [ti-community-infra/tichi](https://github.com/ti-community-infra/tichi/issues/new?title=Prow%20issue:) repository.
</details>
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-21T07:22:57Z
body: /run-pull-e2e-kind-v2
author_association: NONE
user: login: liubog2008
created_at: 2025-05-21T09:38:42Z
body: /lgtm
author_association: MEMBER
user: login: ti-chi-bot[bot]
created_at: 2025-05-21T09:38:46Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/pingcap/tidb-operator/pull/6202#issuecomment-2897289355" title="LGTM">liubog2008</a>*

The full list of commands accepted by this bot can be found [here](https://prow.tidb.net/command-help?repo=pingcap%2Ftidb-operator).

The pull request process is described [here](https://book.prow.tidb.net/#/workflows/pr)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/pingcap/tidb-operator/blob/feature/v2/OWNERS)~~ [liubog2008]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: ti-chi-bot[bot]
created_at: 2025-05-21T09:38:47Z
body: [LGTM Timeline notifier]
---
Timeline:

- `2025-05-21 07:17:57.669074817 +0000 UTC m=+159714.770242024`: :ballot_box_with_check: agreed by [liubog2008](https://github.com/liubog2008).
- `2025-05-21 07:22:47.4970429 +0000 UTC m=+160004.598210122`: :heavy_multiplication_x::repeat: reset by [ti-chi-bot[bot]](https://github.com/ti-chi-bot[bot]).
- `2025-05-21 09:38:46.801997714 +0000 UTC m=+168163.903164936`: :ballot_box_with_check: agreed by [liubog2008](https://github.com/liubog2008).
author_association: CONTRIBUTOR