diff --git a/api/core/v1alpha1/tiproxy_types.go b/api/core/v1alpha1/tiproxy_types.go index ad2d79c9a72..68598713424 100644 --- a/api/core/v1alpha1/tiproxy_types.go +++ b/api/core/v1alpha1/tiproxy_types.go @@ -205,13 +205,37 @@ type TiProxyServer struct { type TiProxyPorts struct { // Client defines port for TiProxy's SQL service. - Client *Port `json:"client,omitempty"` + Client *TiProxyPortOrRange `json:"client,omitempty"` // API defines port for TiProxy API service. API *Port `json:"api,omitempty"` // Peer defines port for TiProxy's peer service. Peer *Port `json:"peer,omitempty"` } +// TiProxyPortOrRange defines the main SQL port and optional extra listening range. +// The operator continues to use Port for Pod probes and the internal Service. +// Range is only propagated to TiProxy's proxy.port-range config. +// +kubebuilder:validation:XValidation:rule="!has(self.range) || self.range.start <= self.range.end",message="range.start must be less than or equal to range.end" +type TiProxyPortOrRange struct { + // Port defines the main SQL port exposed by the TiProxy Pod and Service. + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 + Port *int32 `json:"port,omitempty"` + + // Range defines additional SQL ports listened by TiProxy itself. + Range *TiProxyPortRange `json:"range,omitempty"` +} + +type TiProxyPortRange struct { + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 + Start int32 `json:"start"` + + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 + End int32 `json:"end"` +} + type TiProxyProbes struct { // Readiness defines the readiness probe for TiProxy. // The default handler is a TCP socket on the client port. diff --git a/api/core/v1alpha1/zz_generated.deepcopy.go b/api/core/v1alpha1/zz_generated.deepcopy.go index 48f5e451113..d4f589e26d6 100644 --- a/api/core/v1alpha1/zz_generated.deepcopy.go +++ b/api/core/v1alpha1/zz_generated.deepcopy.go @@ -4935,13 +4935,55 @@ func (in *TiProxyList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TiProxyPortOrRange) DeepCopyInto(out *TiProxyPortOrRange) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } + if in.Range != nil { + in, out := &in.Range, &out.Range + *out = new(TiProxyPortRange) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TiProxyPortOrRange. +func (in *TiProxyPortOrRange) DeepCopy() *TiProxyPortOrRange { + if in == nil { + return nil + } + out := new(TiProxyPortOrRange) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TiProxyPortRange) DeepCopyInto(out *TiProxyPortRange) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TiProxyPortRange. +func (in *TiProxyPortRange) DeepCopy() *TiProxyPortRange { + if in == nil { + return nil + } + out := new(TiProxyPortRange) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TiProxyPorts) DeepCopyInto(out *TiProxyPorts) { *out = *in if in.Client != nil { in, out := &in.Client, &out.Client - *out = new(Port) - **out = **in + *out = new(TiProxyPortOrRange) + (*in).DeepCopyInto(*out) } if in.API != nil { in, out := &in.API, &out.API diff --git a/manifests/crd/core.pingcap.com_tiproxies.yaml b/manifests/crd/core.pingcap.com_tiproxies.yaml index 3b5ef3248df..663c72dabb6 100644 --- a/manifests/crd/core.pingcap.com_tiproxies.yaml +++ b/manifests/crd/core.pingcap.com_tiproxies.yaml @@ -8538,11 +8538,34 @@ spec: description: Client defines port for TiProxy's SQL service. properties: port: + description: Port defines the main SQL port exposed by + the TiProxy Pod and Service. format: int32 + maximum: 65535 + minimum: 1 type: integer - required: - - port + range: + description: Range defines additional SQL ports listened + by TiProxy itself. + properties: + end: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + start: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - end + - start + type: object type: object + x-kubernetes-validations: + - message: range.start must be less than or equal to range.end + rule: '!has(self.range) || self.range.start <= self.range.end' peer: description: Peer defines port for TiProxy's peer service. properties: diff --git a/manifests/crd/core.pingcap.com_tiproxygroups.yaml b/manifests/crd/core.pingcap.com_tiproxygroups.yaml index e08590f7b7d..3842064150b 100644 --- a/manifests/crd/core.pingcap.com_tiproxygroups.yaml +++ b/manifests/crd/core.pingcap.com_tiproxygroups.yaml @@ -8836,11 +8836,35 @@ spec: service. properties: port: + description: Port defines the main SQL port exposed + by the TiProxy Pod and Service. format: int32 + maximum: 65535 + minimum: 1 type: integer - required: - - port + range: + description: Range defines additional SQL ports + listened by TiProxy itself. + properties: + end: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + start: + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - end + - start + type: object type: object + x-kubernetes-validations: + - message: range.start must be less than or equal + to range.end + rule: '!has(self.range) || self.range.start <= self.range.end' peer: description: Peer defines port for TiProxy's peer service. diff --git a/pkg/apiutil/core/v1alpha1/tiproxy.go b/pkg/apiutil/core/v1alpha1/tiproxy.go index b8fae44db0d..1a5de787982 100644 --- a/pkg/apiutil/core/v1alpha1/tiproxy.go +++ b/pkg/apiutil/core/v1alpha1/tiproxy.go @@ -23,7 +23,9 @@ import ( func TiProxyGroupClientPort(proxyg *v1alpha1.TiProxyGroup) int32 { if proxyg.Spec.Template.Spec.Server.Ports.Client != nil { - return proxyg.Spec.Template.Spec.Server.Ports.Client.Port + if proxyg.Spec.Template.Spec.Server.Ports.Client.Port != nil { + return *proxyg.Spec.Template.Spec.Server.Ports.Client.Port + } } return v1alpha1.DefaultTiProxyPortClient } @@ -44,11 +46,23 @@ func TiProxyGroupPeerPort(proxyg *v1alpha1.TiProxyGroup) int32 { func TiProxyClientPort(tiproxy *v1alpha1.TiProxy) int32 { if tiproxy.Spec.Server.Ports.Client != nil { - return tiproxy.Spec.Server.Ports.Client.Port + if tiproxy.Spec.Server.Ports.Client.Port != nil { + return *tiproxy.Spec.Server.Ports.Client.Port + } } return v1alpha1.DefaultTiProxyPortClient } +func TiProxyClientPortRange(tiproxy *v1alpha1.TiProxy) []int { + if tiproxy.Spec.Server.Ports.Client == nil || tiproxy.Spec.Server.Ports.Client.Range == nil { + return nil + } + return []int{ + int(tiproxy.Spec.Server.Ports.Client.Range.Start), + int(tiproxy.Spec.Server.Ports.Client.Range.End), + } +} + func TiProxyAPIPort(tiproxy *v1alpha1.TiProxy) int32 { if tiproxy.Spec.Server.Ports.API != nil { return tiproxy.Spec.Server.Ports.API.Port diff --git a/pkg/apiutil/core/v1alpha1/tiproxy_test.go b/pkg/apiutil/core/v1alpha1/tiproxy_test.go new file mode 100644 index 00000000000..f3f9110afba --- /dev/null +++ b/pkg/apiutil/core/v1alpha1/tiproxy_test.go @@ -0,0 +1,57 @@ +// 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 coreutil + +import ( + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/utils/ptr" + + "github.com/pingcap/tidb-operator/api/v2/core/v1alpha1" +) + +func TestTiProxyClientPort(t *testing.T) { + tiproxy := &v1alpha1.TiProxy{} + require.Equal(t, int32(v1alpha1.DefaultTiProxyPortClient), TiProxyClientPort(tiproxy)) + + tiproxy.Spec.Server.Ports.Client = &v1alpha1.TiProxyPortOrRange{ + Range: &v1alpha1.TiProxyPortRange{ + Start: 10000, + End: 10002, + }, + } + require.Equal(t, int32(v1alpha1.DefaultTiProxyPortClient), TiProxyClientPort(tiproxy)) + require.Equal(t, []int{10000, 10002}, TiProxyClientPortRange(tiproxy)) + + tiproxy.Spec.Server.Ports.Client.Port = ptr.To[int32](7000) + require.Equal(t, int32(7000), TiProxyClientPort(tiproxy)) +} + +func TestTiProxyGroupClientPort(t *testing.T) { + proxyg := &v1alpha1.TiProxyGroup{} + require.Equal(t, int32(v1alpha1.DefaultTiProxyPortClient), TiProxyGroupClientPort(proxyg)) + + proxyg.Spec.Template.Spec.Server.Ports.Client = &v1alpha1.TiProxyPortOrRange{ + Range: &v1alpha1.TiProxyPortRange{ + Start: 10000, + End: 10002, + }, + } + require.Equal(t, int32(v1alpha1.DefaultTiProxyPortClient), TiProxyGroupClientPort(proxyg)) + + proxyg.Spec.Template.Spec.Server.Ports.Client.Port = ptr.To[int32](7000) + require.Equal(t, int32(7000), TiProxyGroupClientPort(proxyg)) +} diff --git a/pkg/configs/tiproxy/config.go b/pkg/configs/tiproxy/config.go index a1bcbb11fc5..e61cec6c623 100644 --- a/pkg/configs/tiproxy/config.go +++ b/pkg/configs/tiproxy/config.go @@ -27,13 +27,15 @@ import ( ) type Proxy struct { - Address string `toml:"addr"` - AdvertiseAddress string `toml:"advertise-addr"` - PDAddress string `toml:"pd-addrs"` + Address string `toml:"addr,omitempty" json:"addr,omitempty"` + AdvertiseAddress string `toml:"advertise-addr,omitempty" json:"advertise-addr,omitempty"` + PDAddress string `toml:"pd-addrs,omitempty" json:"pd-addrs,omitempty"` + PortRange []int `toml:"port-range,omitempty" json:"port-range,omitempty"` + BackendClusters []BackendCluster `toml:"backend-clusters,omitempty" json:"backend-clusters,omitempty"` } type API struct { - Address string `toml:"addr"` + Address string `toml:"addr,omitempty" json:"addr,omitempty"` } type TLSConfig struct { @@ -54,6 +56,12 @@ type Security struct { SQLTLS TLSConfig `toml:"sql-tls,omitempty"` } +type BackendCluster struct { + Name string `toml:"name,omitempty" json:"name,omitempty"` + PDAddrs string `toml:"pd-addrs,omitempty" json:"pd-addrs,omitempty"` + NSServers []string `toml:"ns-servers,omitempty" json:"ns-servers,omitempty"` +} + // Config is a subset config of TiProxy. // Only TiDB Operator managed fields are defined here. // ref: https://docs.pingcap.com/tidb/stable/tiproxy-configuration/ @@ -73,6 +81,7 @@ func (c *Config) Overlay(cluster *v1alpha1.Cluster, tiproxy *v1alpha1.TiProxy) e c.Proxy.Address = coreutil.ListenAddress(coreutil.TiProxyClientPort(tiproxy)) c.Proxy.AdvertiseAddress = getAdvertiseAddress(cluster, tiproxy) c.Proxy.PDAddress = stringutil.RemoveHTTPPrefix(cluster.Status.PD) + c.Proxy.PortRange = coreutil.TiProxyClientPortRange(tiproxy) c.API.Address = coreutil.ListenAddress(coreutil.TiProxyAPIPort(tiproxy)) if coreutil.IsTLSClusterEnabled(cluster) { @@ -128,6 +137,10 @@ func (c *Config) Validate() error { fields = append(fields, "proxy.pd-address") } + if len(c.Proxy.PortRange) > 0 { + fields = append(fields, "proxy.port-range") + } + if c.API.Address != "" { fields = append(fields, "api.address") } diff --git a/pkg/configs/tiproxy/config_test.go b/pkg/configs/tiproxy/config_test.go index 64c27fd3bac..a76c5f68f7d 100644 --- a/pkg/configs/tiproxy/config_test.go +++ b/pkg/configs/tiproxy/config_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "github.com/pingcap/tidb-operator/api/v2/core/v1alpha1" ) @@ -36,6 +37,18 @@ func TestValidate(t *testing.T) { config: &Config{}, wantErr: false, }, + { + name: "valid config with backend clusters", + config: &Config{ + Proxy: Proxy{ + BackendClusters: []BackendCluster{{ + Name: "cluster-a", + PDAddrs: "127.0.0.1:2379", + }}, + }, + }, + wantErr: false, + }, { name: "invalid config with managed fields", config: &Config{ @@ -43,6 +56,7 @@ func TestValidate(t *testing.T) { Address: "[::]:4000", AdvertiseAddress: "tiproxy-0.tiproxy-peer.default.svc", PDAddress: "pd:2379", + PortRange: []int{10000, 10002}, }, API: API{ Address: "[::]:3080", @@ -60,6 +74,7 @@ func TestValidate(t *testing.T) { "proxy.address", "proxy.advertise-address", "proxy.pd-address", + "proxy.port-range", "api.address", "security.server-sql-tls.cert", "security.server-sql-tls.key", @@ -123,6 +138,80 @@ func TestOverlay(t *testing.T) { }, wantErr: false, }, + { + name: "basic config without local pd", + cluster: &v1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns1", + Name: "db", + }, + }, + tiproxy: &v1alpha1.TiProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns1", + Name: "db-foo", + }, + Spec: v1alpha1.TiProxySpec{ + Subdomain: "db-tiproxy-peer", + }, + }, + want: &Config{ + Proxy: Proxy{ + Address: "[::]:6000", + AdvertiseAddress: "db-tiproxy-foo.db-tiproxy-peer.ns1.svc", + }, + API: API{ + Address: "[::]:3080", + }, + }, + wantErr: false, + }, + { + name: "config with client port range", + cluster: &v1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns1", + Name: "db", + }, + Status: v1alpha1.ClusterStatus{ + PD: "http://db-pd.ns1:2379", + }, + }, + tiproxy: &v1alpha1.TiProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns1", + Name: "db-foo", + }, + Spec: v1alpha1.TiProxySpec{ + Subdomain: "tiproxy-peer", + TiProxyTemplateSpec: v1alpha1.TiProxyTemplateSpec{ + Server: v1alpha1.TiProxyServer{ + Ports: v1alpha1.TiProxyPorts{ + Client: &v1alpha1.TiProxyPortOrRange{ + Port: ptr.To[int32](7000), + Range: &v1alpha1.TiProxyPortRange{ + Start: 10000, + End: 10002, + }, + }, + }, + }, + }, + }, + }, + want: &Config{ + Proxy: Proxy{ + Address: "[::]:7000", + AdvertiseAddress: "db-tiproxy-foo.tiproxy-peer.ns1.svc", + PDAddress: "db-pd.ns1:2379", + PortRange: []int{10000, 10002}, + }, + API: API{ + Address: "[::]:3080", + }, + }, + wantErr: false, + }, { name: "config with cluster TLS enabled", cluster: &v1alpha1.Cluster{ diff --git a/pkg/controllers/tiproxy/builder.go b/pkg/controllers/tiproxy/builder.go index 106219be402..eb68f104fe4 100644 --- a/pkg/controllers/tiproxy/builder.go +++ b/pkg/controllers/tiproxy/builder.go @@ -39,9 +39,6 @@ func (r *Reconciler) NewRunner(state *tasks.ReconcileContext, reporter task.Task task.IfBreak(common.CondClusterIsDeleting(state), common.TaskInstanceFinalizerDel[scope.TiProxy](state, r.Client, common.DefaultInstanceSubresourceLister), ), - // return if cluster's status is not updated - task.IfBreak(common.CondClusterPDAddrIsNotRegistered(state)), - task.IfBreak(common.CondObjectIsDeleting[scope.TiProxy](state), common.TaskInstanceFinalizerDel[scope.TiProxy](state, r.Client, common.DefaultInstanceSubresourceLister), // TODO(liubo02): if the finalizer has been removed, no need to update status @@ -68,10 +65,12 @@ func (r *Reconciler) NewRunner(state *tasks.ReconcileContext, reporter task.Task tasks.TaskConfigMap(state, r.Client), common.TaskPVC[scope.TiProxy](state, r.Client, r.VolumeModifierFactory, tasks.PVCNewer()), tasks.TaskPod(state, r.Client), - common.TaskServerLabels[scope.TiProxy](state, r.Client, r.PDClientManager, func(ctx context.Context, labels map[string]string) error { - // TODO(liubo02): compare before setting - return state.TiProxyClient.SetLabels(ctx, labels) - }), + task.IfNot(common.CondClusterPDAddrIsNotRegistered(state), + common.TaskServerLabels[scope.TiProxy](state, r.Client, r.PDClientManager, func(ctx context.Context, labels map[string]string) error { + // TODO(liubo02): compare before setting + return state.TiProxyClient.SetLabels(ctx, labels) + }), + ), common.TaskInstanceConditionSynced[scope.TiProxy](state), common.TaskInstanceConditionReady[scope.TiProxy](state), common.TaskInstanceConditionRunning[scope.TiProxy](state), diff --git a/pkg/controllers/tiproxy/tasks/cm_test.go b/pkg/controllers/tiproxy/tasks/cm_test.go index 0bdf332a385..421101ce02b 100644 --- a/pkg/controllers/tiproxy/tasks/cm_test.go +++ b/pkg/controllers/tiproxy/tasks/cm_test.go @@ -45,6 +45,16 @@ func TestTaskConfigMap(t *testing.T) { }{ { desc: "no config", + state: &ReconcileContext{ + State: &state{ + tiproxy: fake.FakeObj[v1alpha1.TiProxy]("aaa-xxx"), + cluster: fake.FakeObj[v1alpha1.Cluster]("cluster"), + }, + }, + expectedStatus: task.SComplete, + }, + { + desc: "no config with local pd", state: &ReconcileContext{ State: &state{ tiproxy: fake.FakeObj[v1alpha1.TiProxy]("aaa-xxx"), @@ -88,6 +98,24 @@ func TestTaskConfigMap(t *testing.T) { }, expectedStatus: task.SFail, }, + { + desc: "user managed backend clusters in config", + state: &ReconcileContext{ + State: &state{ + tiproxy: fake.FakeObj("aaa-xxx", func(obj *v1alpha1.TiProxy) *v1alpha1.TiProxy { + obj.Spec.Config = `[[proxy.backend-clusters]] +name = "cluster-a" +pd-addrs = "127.0.0.1:2379"` + return obj + }), + cluster: fake.FakeObj("cluster", func(obj *v1alpha1.Cluster) *v1alpha1.Cluster { + obj.Status.PD = fakePDAddr + return obj + }), + }, + }, + expectedStatus: task.SComplete, + }, { desc: "has config map", state: &ReconcileContext{ diff --git a/pkg/reloadable/tiproxy.go b/pkg/reloadable/tiproxy.go index 721da820b67..0ce4f298b57 100644 --- a/pkg/reloadable/tiproxy.go +++ b/pkg/reloadable/tiproxy.go @@ -112,7 +112,8 @@ func equalTiProxyTemplate(c, p *v1alpha1.TiProxyTemplate) bool { p = convertTiProxyTemplate(p) c = convertTiProxyTemplate(c) // not equal only when current strategy is Restart and config is changed - if c.Spec.UpdateStrategy.Config == v1alpha1.ConfigUpdateStrategyRestart && p.Spec.Config != c.Spec.Config { + if c.Spec.UpdateStrategy.Config == v1alpha1.ConfigUpdateStrategyRestart && + p.Spec.Config != c.Spec.Config { return false } diff --git a/tests/e2e/tiproxy/tiproxy.go b/tests/e2e/tiproxy/tiproxy.go index 9ae5c2cd603..c5616dc6869 100644 --- a/tests/e2e/tiproxy/tiproxy.go +++ b/tests/e2e/tiproxy/tiproxy.go @@ -18,13 +18,17 @@ import ( "context" "fmt" + "github.com/BurntSushi/toml" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/pingcap/tidb-operator/api/v2/core/v1alpha1" + tiproxycfg "github.com/pingcap/tidb-operator/v2/pkg/configs/tiproxy" "github.com/pingcap/tidb-operator/v2/pkg/runtime" "github.com/pingcap/tidb-operator/v2/pkg/runtime/scope" "github.com/pingcap/tidb-operator/v2/tests/e2e/data" @@ -216,6 +220,58 @@ var _ = ginkgo.Describe("TiProxy", label.TiProxy, func() { f.Must(waiter.WaitForPodsRecreated(ctx, f.Client, runtime.FromTiProxyGroup(proxyg), *changeTime, waiter.LongTaskTimeout)) f.WaitForTiProxyGroupReady(ctx, proxyg) }) + + ginkgo.It("support tiproxy without any pd configured", label.P1, label.FeatureHotReload, func(ctx context.Context) { + proxyg := f.MustCreateTiProxy( + ctx, + data.WithReplicas[scope.TiProxyGroup](1), + data.WithHotReloadPolicyForTiProxy(), + ) + + gomega.Eventually(func() error { + proxies := &v1alpha1.TiProxyList{} + if err := f.Client.List(ctx, proxies, client.InNamespace(proxyg.Namespace), client.MatchingLabels{ + v1alpha1.LabelKeyCluster: proxyg.Spec.Cluster.Name, + v1alpha1.LabelKeyGroup: proxyg.Name, + v1alpha1.LabelKeyComponent: v1alpha1.LabelValComponentTiProxy, + }); err != nil { + return err + } + if len(proxies.Items) != 1 { + return fmt.Errorf("expected 1 TiProxy, got %d", len(proxies.Items)) + } + cond := meta.FindStatusCondition(proxies.Items[0].Status.Conditions, v1alpha1.CondSynced) + if cond == nil || cond.Status != metav1.ConditionTrue { + return fmt.Errorf("TiProxy is not synced: %#v", cond) + } + return nil + }).WithTimeout(waiter.LongTaskTimeout).WithPolling(waiter.Poll).Should(gomega.Succeed()) + + gomega.Eventually(func() error { + cms := &corev1.ConfigMapList{} + if err := f.Client.List(ctx, cms, client.InNamespace(proxyg.Namespace), client.MatchingLabels{ + v1alpha1.LabelKeyCluster: proxyg.Spec.Cluster.Name, + v1alpha1.LabelKeyComponent: v1alpha1.LabelValComponentTiProxy, + }); err != nil { + return err + } + if len(cms.Items) != 1 { + return fmt.Errorf("expected 1 TiProxy ConfigMap, got %d", len(cms.Items)) + } + + var cfg tiproxycfg.Config + if err := toml.Unmarshal([]byte(cms.Items[0].Data[v1alpha1.FileNameConfig]), &cfg); err != nil { + return err + } + if cfg.Proxy.PDAddress != "" { + return fmt.Errorf("expected empty proxy.pd-addrs, got %q", cfg.Proxy.PDAddress) + } + if len(cfg.Proxy.BackendClusters) != 0 { + return fmt.Errorf("expected no backend clusters, got %#v", cfg.Proxy.BackendClusters) + } + return nil + }).WithTimeout(waiter.LongTaskTimeout).WithPolling(waiter.Poll).Should(gomega.Succeed()) + }) }) ginkgo.Context("TLS", label.P0, label.FeatureTLS, func() {