diff --git a/CHANGELOG.md b/CHANGELOG.md index cc50d969c8..d03d273876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ TiUP Changelog +## [1.12.4] 2023-7-13 + +### Fix + +- Fix cannot show tiflash uptime in `tiup-cluster` (#2227, @nexustar) + +### Improvement + +- Remove tcp_port for tiflash in `tiup-cluster` and `tiup-playground` (#2220, @zanmato1984) + +## [1.12.3] 2023-6-14 + +### Fixes + +- Fix cannot edit manage_host on an exist cluster in `tiup-cluster` (#2210, @nexustar) +- Fix still use host instead of manage_host in `tiup-cluster` (#2206 #2207, @nexustar) + +### Improvement + +- Check if the compnoent exists when uninstall in `tiup` (#2209, @srstack) + ## [1.12.2] 2023-5-19 ### Notes diff --git a/cmd/uninstall.go b/cmd/uninstall.go index 058727cd4c..0b243bc6cc 100644 --- a/cmd/uninstall.go +++ b/cmd/uninstall.go @@ -51,7 +51,7 @@ which is used to uninstall tiup. teleCommand = cmd.CommandPath() env := environment.GlobalEnv() if self { - deletable := []string{"bin", "manifest", "manifests", "components", "storage/cluster/packages"} + deletable := []string{"storage/cluster/packages", "components", "manifests", "manifest", "bin"} for _, dir := range deletable { if err := os.RemoveAll(env.Profile().Path(dir)); err != nil { return errors.Trace(err) @@ -86,6 +86,9 @@ func removeComponents(env *environment.Environment, specs []string, all bool) er if strings.Contains(spec, ":") { parts := strings.SplitN(spec, ":", 2) // after this version is deleted, component will have no version left. delete the whole component dir directly + if !utils.IsExist(env.LocalPath(localdata.ComponentParentDir, parts[0])) { + return errors.Trace(fmt.Errorf("component `%s` is not installed, please use `tiup list %s` to check", parts[0], parts[0])) + } dir, err := os.ReadDir(env.LocalPath(localdata.ComponentParentDir, parts[0])) if err != nil { return errors.Trace(err) @@ -99,6 +102,7 @@ func removeComponents(env *environment.Environment, specs []string, all bool) er } else { paths = append(paths, env.LocalPath(localdata.ComponentParentDir, parts[0], parts[1])) } + // if no more version left, delete the whole component dir if len(dir)-len(paths) < 1 { paths = append(paths, env.LocalPath(localdata.ComponentParentDir, parts[0])) } @@ -110,6 +114,10 @@ func removeComponents(env *environment.Environment, specs []string, all bool) er paths = append(paths, env.LocalPath(localdata.ComponentParentDir, spec)) } for _, path := range paths { + if !utils.IsExist(path) { + return errors.Trace(fmt.Errorf("component `%s` is not installed, please check", spec)) + } + fmt.Println(path) if err := os.RemoveAll(path); err != nil { return errors.Trace(err) } diff --git a/components/dm/command/prune.go b/components/dm/command/prune.go index c6bb13b97d..6b7c50cbc7 100644 --- a/components/dm/command/prune.go +++ b/components/dm/command/prune.go @@ -76,7 +76,7 @@ func clearOutDatedEtcdInfo(clusterName string, metadata *spec.Metadata, opt oper if err != nil { return err } - dmMasterClient := api.NewDMMasterClient(topo.GetMasterList(), 10*time.Second, tlsCfg) + dmMasterClient := api.NewDMMasterClient(topo.GetMasterListWithManageHost(), 10*time.Second, tlsCfg) registeredMasters, registeredWorkers, err := dmMasterClient.GetRegisteredMembers() if err != nil { return err diff --git a/components/dm/command/scale_in.go b/components/dm/command/scale_in.go index 91eac16176..e9bd661ab1 100644 --- a/components/dm/command/scale_in.go +++ b/components/dm/command/scale_in.go @@ -27,6 +27,7 @@ import ( "github.com/pingcap/tiup/pkg/cluster/spec" "github.com/pingcap/tiup/pkg/cluster/task" "github.com/pingcap/tiup/pkg/set" + "github.com/pingcap/tiup/pkg/utils" "github.com/spf13/cobra" ) @@ -126,7 +127,7 @@ func ScaleInDMCluster( var dmMasterEndpoint []string for _, instance := range (&dm.DMMasterComponent{Topology: topo}).Instances() { if !deletedNodes.Exist(instance.ID()) { - dmMasterEndpoint = append(dmMasterEndpoint, operator.Addr(instance)) + dmMasterEndpoint = append(dmMasterEndpoint, utils.JoinHostPort(instance.GetManageHost(), instance.GetPort())) } } diff --git a/components/dm/spec/topology_dm.go b/components/dm/spec/topology_dm.go index 4e5de5327c..2f22ec35be 100644 --- a/components/dm/spec/topology_dm.go +++ b/components/dm/spec/topology_dm.go @@ -122,7 +122,7 @@ func AllDMComponentNames() (roles []string) { // MasterSpec represents the Master topology specification in topology.yaml type MasterSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -205,7 +205,7 @@ func (s *MasterSpec) GetAdvertisePeerURL(enableTLS bool) string { // WorkerSpec represents the Master topology specification in topology.yaml type WorkerSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -675,7 +675,7 @@ func (s *Specification) BaseTopo() *spec.BaseTopo { return &spec.BaseTopo{ GlobalOptions: &s.GlobalOptions, MonitoredOptions: s.GetMonitoredOptions(), - MasterList: s.GetMasterList(), + MasterList: s.GetMasterListWithManageHost(), Monitors: s.Monitors, Grafanas: s.Grafanas, Alertmanagers: s.Alertmanagers, @@ -701,12 +701,16 @@ func (s *Specification) MergeTopo(rhs spec.Topology) spec.Topology { return s.Merge(other) } -// GetMasterList returns a list of Master API hosts of the current cluster -func (s *Specification) GetMasterList() []string { +// GetMasterListWithManageHost returns a list of Master API hosts of the current cluster +func (s *Specification) GetMasterListWithManageHost() []string { var masterList []string for _, master := range s.Masters { - masterList = append(masterList, utils.JoinHostPort(master.Host, master.Port)) + host := master.Host + if master.ManageHost != "" { + host = master.ManageHost + } + masterList = append(masterList, utils.JoinHostPort(host, master.Port)) } return masterList diff --git a/components/playground/instance/tiflash.go b/components/playground/instance/tiflash.go index 72f815cd23..cb73f2d722 100644 --- a/components/playground/instance/tiflash.go +++ b/components/playground/instance/tiflash.go @@ -138,7 +138,6 @@ func (inst *TiFlashInstance) Start(ctx context.Context, version utils.Version) e fmt.Sprintf("--tmp_path=%s", filepath.Join(inst.Dir, "tmp")), fmt.Sprintf("--path=%s", filepath.Join(inst.Dir, "data")), fmt.Sprintf("--listen_host=%s", inst.Host), - fmt.Sprintf("--tcp_port=%d", inst.TCPPort), fmt.Sprintf("--logger.log=%s", inst.LogFile()), fmt.Sprintf("--logger.errorlog=%s", filepath.Join(inst.Dir, "tiflash_error.log")), fmt.Sprintf("--status.metrics_port=%d", inst.StatusPort), diff --git a/components/playground/instance/tiflash_pre7_config.go b/components/playground/instance/tiflash_pre7_config.go index cd3e04be10..3481540c18 100644 --- a/components/playground/instance/tiflash_pre7_config.go +++ b/components/playground/instance/tiflash_pre7_config.go @@ -34,10 +34,10 @@ const tiflashMarkCacheSizeOld = `mark_cache_size = 5368709120` const tiflashConfigOld = ` default_profile = "default" display_name = "TiFlash" -%[2]s +http_port = %[2]d listen_host = "0.0.0.0" -path = "%[5]s" tcp_port = %[3]d +path = "%[5]s" tmp_path = "%[6]s" %[14]s %[13]s @@ -109,11 +109,11 @@ func writeTiFlashConfigOld(w io.Writer, version utils.Version, tcpPort, httpPort var conf string if tidbver.TiFlashNotNeedSomeConfig(version.String()) { - conf = fmt.Sprintf(tiflashConfigOld, pdAddrs, fmt.Sprintf(`http_port = %d`, httpPort), tcpPort, + conf = fmt.Sprintf(tiflashConfigOld, pdAddrs, httpPort, tcpPort, deployDir, dataDir, tmpDir, logDir, servicePort, metricsPort, ip, strings.Join(tidbStatusAddrs, ","), clusterManagerPath, "", "") } else { - conf = fmt.Sprintf(tiflashConfigOld, pdAddrs, fmt.Sprintf(`http_port = %d`, httpPort), tcpPort, + conf = fmt.Sprintf(tiflashConfigOld, pdAddrs, httpPort, tcpPort, deployDir, dataDir, tmpDir, logDir, servicePort, metricsPort, ip, strings.Join(tidbStatusAddrs, ","), clusterManagerPath, tiflashDaemonConfigOld, tiflashMarkCacheSizeOld) } diff --git a/components/playground/playground.go b/components/playground/playground.go index 7be411cd52..af4573c813 100644 --- a/components/playground/playground.go +++ b/components/playground/playground.go @@ -913,7 +913,7 @@ func (p *Playground) bootCluster(ctx context.Context, env *environment.Environme return fmt.Errorf("TiDB cluster doesn't support disaggregated mode in version %s", options.Version) } if !tidbver.TiFlashPlaygroundNewStartMode(options.Version) { - // For simplicitly, currently we only implemented disagg mode when TiFlash can run without config. + // For simplicity, currently we only implemented disagg mode when TiFlash can run without config. return fmt.Errorf("TiUP playground only supports disaggregated mode for TiDB cluster >= v7.1.0 (or nightly)") } diff --git a/embed/examples/cluster/minimal.yaml b/embed/examples/cluster/minimal.yaml index 7f373b1514..5a26fda29b 100644 --- a/embed/examples/cluster/minimal.yaml +++ b/embed/examples/cluster/minimal.yaml @@ -182,6 +182,7 @@ tiflash_servers: # # SSH port of the server. # ssh_port: 22 # # TiFlash TCP Service port. + # # Since 7.1.0, it is not actually listened, and only being used as part of the instance identity. # tcp_port: 9000 # # TiFlash raft service and coprocessor service listening address. # flash_service_port: 3930 diff --git a/embed/examples/cluster/multi-dc.yaml b/embed/examples/cluster/multi-dc.yaml index 06fdae3af9..666936f7bc 100644 --- a/embed/examples/cluster/multi-dc.yaml +++ b/embed/examples/cluster/multi-dc.yaml @@ -235,6 +235,7 @@ tiflash_servers: # # SSH port of the server. # ssh_port: 22 # # TiFlash TCP Service port. + # # Since 7.1.0, it is not actually listened, and only being used as part of the instance identity. # tcp_port: 9000 # # TiFlash raft service and coprocessor service listening address. # flash_service_port: 3930 diff --git a/embed/examples/cluster/topology.example.yaml b/embed/examples/cluster/topology.example.yaml index f476316f2b..1525c5284a 100644 --- a/embed/examples/cluster/topology.example.yaml +++ b/embed/examples/cluster/topology.example.yaml @@ -224,6 +224,7 @@ tiflash_servers: # # SSH port of the server. # ssh_port: 22 # # TiFlash TCP Service port. + # # Since 7.1.0, it is not actually listened, and only being used as part of the instance identity. tcp_port: 9000 # # TiFlash raft service and coprocessor service listening address. flash_service_port: 3930 diff --git a/pkg/cluster/ansible/service_test.go b/pkg/cluster/ansible/service_test.go index a119f2455f..0c5f6044ec 100644 --- a/pkg/cluster/ansible/service_test.go +++ b/pkg/cluster/ansible/service_test.go @@ -23,7 +23,6 @@ default_profile = "default" display_name = "TiFlash" listen_host = "0.0.0.0" path = "/data1/test-cluster/leiysky-ansible-test-deploy/tiflash/data/db" -tcp_port = 11315 tmp_path = "/data1/test-cluster/leiysky-ansible-test-deploy/tiflash/data/db/tmp" [flash] diff --git a/pkg/cluster/manager/check.go b/pkg/cluster/manager/check.go index 75b5776fd6..9790adc302 100644 --- a/pkg/cluster/manager/check.go +++ b/pkg/cluster/manager/check.go @@ -695,7 +695,7 @@ func (m *Manager) checkRegionsInfo(clusterName string, topo *spec.Specification, } pdClient := api.NewPDClient( context.WithValue(context.TODO(), logprinter.ContextKeyLogger, m.logger), - topo.GetPDList(), + topo.GetPDListWithManageHost(), time.Second*time.Duration(gOpt.APITimeout), tlsConfig, ) diff --git a/pkg/cluster/manager/display.go b/pkg/cluster/manager/display.go index ae8e741ca9..8965b14bb0 100644 --- a/pkg/cluster/manager/display.go +++ b/pkg/cluster/manager/display.go @@ -42,7 +42,6 @@ import ( "github.com/pingcap/tiup/pkg/set" "github.com/pingcap/tiup/pkg/tui" "github.com/pingcap/tiup/pkg/utils" - "go.uber.org/zap" ) // DisplayOption represents option of display command @@ -233,7 +232,7 @@ func (m *Manager) Display(dopt DisplayOption, opt operator.Options) error { continue } if strings.HasPrefix(v.Status, "Up") || strings.HasPrefix(v.Status, "Healthy") { - instAddr := utils.JoinHostPort(v.Host, v.Port) + instAddr := utils.JoinHostPort(v.ManageHost, v.Port) masterActive = append(masterActive, instAddr) } } @@ -610,8 +609,9 @@ func (m *Manager) GetClusterTopology(dopt DisplayOption, opt operator.Options) ( e, found := ctxt.GetInner(ctx).GetExecutor(ins.GetManageHost()) if found { var active string + var systemdSince time.Duration nctx := checkpoint.NewContext(ctx) - active, memory, _ = operator.GetServiceStatus(nctx, e, ins.ServiceName()) + active, memory, systemdSince, _ = operator.GetServiceStatus(nctx, e, ins.ServiceName()) if status == "-" { if active == "active" { status = "Up" @@ -620,7 +620,7 @@ func (m *Manager) GetClusterTopology(dopt DisplayOption, opt operator.Options) ( } } if dopt.ShowUptime && since == "-" { - since = formatInstanceSince(parseSystemctlSince(active)) + since = formatInstanceSince(systemdSince) } } } @@ -733,37 +733,6 @@ func formatInstanceSince(uptime time.Duration) string { return strings.Join(parts, "") } -// `systemctl status xxx.service` returns as below -// Active: active (running) since Sat 2021-03-27 10:51:11 CST; 41min ago -func parseSystemctlSince(str string) (dur time.Duration) { - // if service is not found or other error, don't need to parse it - if str == "" { - return 0 - } - defer func() { - if dur == 0 { - zap.L().Warn("failed to parse systemctl since", zap.String("value", str)) - } - }() - parts := strings.Split(str, ";") - if len(parts) != 2 { - return - } - parts = strings.Split(parts[0], " ") - if len(parts) < 3 { - return - } - - dateStr := strings.Join(parts[len(parts)-3:], " ") - - tm, err := time.Parse("2006-01-02 15:04:05 MST", dateStr) - if err != nil { - return - } - - return time.Since(tm) -} - // SetSSHKeySet set ssh key set. func SetSSHKeySet(ctx context.Context, privateKeyPath string, publicKeyPath string) error { ctxt.GetInner(ctx).PrivateKeyPath = privateKeyPath @@ -809,13 +778,8 @@ func (m *Manager) DisplayDashboardInfo(clusterName string, timeout time.Duration return err } - pdEndpoints := make([]string, 0) - for _, pd := range metadata.Topology.PDServers { - pdEndpoints = append(pdEndpoints, utils.JoinHostPort(pd.Host, pd.ClientPort)) - } - ctx := context.WithValue(context.Background(), logprinter.ContextKeyLogger, m.logger) - pdAPI := api.NewPDClient(ctx, pdEndpoints, timeout, tlsCfg) + pdAPI := api.NewPDClient(ctx, metadata.Topology.GetPDListWithManageHost(), timeout, tlsCfg) dashboardAddr, err := pdAPI.GetDashboardAddress() if err != nil { return fmt.Errorf("failed to retrieve TiDB Dashboard instance from PD: %s", err) diff --git a/pkg/cluster/operation/check.go b/pkg/cluster/operation/check.go index 3784bc69a4..f04981518e 100644 --- a/pkg/cluster/operation/check.go +++ b/pkg/cluster/operation/check.go @@ -520,7 +520,7 @@ func CheckServices(ctx context.Context, e ctxt.Executor, host, service string, d return result } - active, _, err := GetServiceStatus(ctx, e, service+".service") + active, _, _, err := GetServiceStatus(ctx, e, service+".service") if err != nil { result.Err = err } diff --git a/pkg/cluster/operation/destroy.go b/pkg/cluster/operation/destroy.go index 0d44613bf3..aeae040e3a 100644 --- a/pkg/cluster/operation/destroy.go +++ b/pkg/cluster/operation/destroy.go @@ -497,7 +497,7 @@ func DestroyClusterTombstone( pdEndpoints = strings.Split(forcePDEndpoints, ",") logger.Warnf("%s is set, using %s as PD endpoints", EnvNamePDEndpointOverwrite, pdEndpoints) } else { - pdEndpoints = cluster.GetPDList() + pdEndpoints = cluster.GetPDListWithManageHost() } var pdClient = api.NewPDClient(ctx, pdEndpoints, 10*time.Second, tlsCfg) diff --git a/pkg/cluster/operation/scale_in.go b/pkg/cluster/operation/scale_in.go index 579b56ac01..50bc246572 100644 --- a/pkg/cluster/operation/scale_in.go +++ b/pkg/cluster/operation/scale_in.go @@ -452,7 +452,7 @@ func scaleInCDC( deferInstances := make([]spec.Instance, 0, 1) for _, instance := range instances { address := instance.(*spec.CDCInstance).GetAddr() - client := api.NewCDCOpenAPIClient(ctx, []string{address}, 5*time.Second, tlsCfg) + client := api.NewCDCOpenAPIClient(ctx, []string{utils.JoinHostPort(instance.GetManageHost(), instance.GetPort())}, 5*time.Second, tlsCfg) capture, err := client.GetCaptureByAddr(address) if err != nil { diff --git a/pkg/cluster/operation/systemd.go b/pkg/cluster/operation/systemd.go index 497d05db22..8c5ba4e2cc 100644 --- a/pkg/cluster/operation/systemd.go +++ b/pkg/cluster/operation/systemd.go @@ -16,10 +16,12 @@ package operator import ( "context" "strings" + "time" "github.com/pingcap/errors" "github.com/pingcap/tiup/pkg/cluster/ctxt" "github.com/pingcap/tiup/pkg/cluster/module" + "go.uber.org/zap" ) // GetServiceStatus return the Acitive line of status. @@ -34,7 +36,7 @@ import ( Mar 09 13:56:19 ip-172-16-5-70 systemd[1]: Started drainer-8249 service. */ -func GetServiceStatus(ctx context.Context, e ctxt.Executor, name string) (active, memory string, err error) { +func GetServiceStatus(ctx context.Context, e ctxt.Executor, name string) (active, memory string, since time.Duration, err error) { c := module.SystemdModuleConfig{ Unit: name, Action: "status", @@ -50,6 +52,7 @@ func GetServiceStatus(ctx context.Context, e ctxt.Executor, name string) (active switch words[0] { case "Active:": active = words[1] + since = parseSystemctlSince(line) case "Memory:": memory = words[1] } @@ -60,3 +63,34 @@ func GetServiceStatus(ctx context.Context, e ctxt.Executor, name string) (active } return } + +// `systemctl status xxx.service` returns as below +// Active: active (running) since Sat 2021-03-27 10:51:11 CST; 41min ago +func parseSystemctlSince(str string) (dur time.Duration) { + // if service is not found or other error, don't need to parse it + if str == "" { + return 0 + } + defer func() { + if dur == 0 { + zap.L().Warn("failed to parse systemctl since", zap.String("value", str)) + } + }() + parts := strings.Split(str, ";") + if len(parts) != 2 { + return + } + parts = strings.Split(parts[0], " ") + if len(parts) < 3 { + return + } + + dateStr := strings.Join(parts[len(parts)-3:], " ") + + tm, err := time.Parse("2006-01-02 15:04:05 MST", dateStr) + if err != nil { + return + } + + return time.Since(tm) +} diff --git a/pkg/cluster/operation/upgrade.go b/pkg/cluster/operation/upgrade.go index 72fbc35149..7976635d38 100644 --- a/pkg/cluster/operation/upgrade.go +++ b/pkg/cluster/operation/upgrade.go @@ -79,7 +79,7 @@ func Upgrade( pdEndpoints = strings.Split(forcePDEndpoints, ",") logger.Warnf("%s is set, using %s as PD endpoints", EnvNamePDEndpointOverwrite, pdEndpoints) } else { - pdEndpoints = topo.(*spec.Specification).GetPDList() + pdEndpoints = topo.(*spec.Specification).GetPDListWithManageHost() } pdClient := api.NewPDClient(ctx, pdEndpoints, 10*time.Second, tlsCfg) origLeaderScheduleLimit, origRegionScheduleLimit, err = increaseScheduleLimit(ctx, pdClient) @@ -143,7 +143,7 @@ func Upgrade( // during the upgrade process, endpoint addresses should not change, so only new the client once. if cdcOpenAPIClient == nil { - cdcOpenAPIClient = api.NewCDCOpenAPIClient(ctx, topo.(*spec.Specification).GetCDCList(), 5*time.Second, tlsCfg) + cdcOpenAPIClient = api.NewCDCOpenAPIClient(ctx, topo.(*spec.Specification).GetCDCListWithManageHost(), 5*time.Second, tlsCfg) } capture, err := cdcOpenAPIClient.GetCaptureByAddr(address) diff --git a/pkg/cluster/spec/alertmanager.go b/pkg/cluster/spec/alertmanager.go index 527bb21927..40f855eb1f 100644 --- a/pkg/cluster/spec/alertmanager.go +++ b/pkg/cluster/spec/alertmanager.go @@ -30,7 +30,7 @@ import ( // AlertmanagerSpec represents the AlertManager topology specification in topology.yaml type AlertmanagerSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -67,6 +67,14 @@ func (s *AlertmanagerSpec) GetMainPort() int { return s.WebPort } +// GetManageHost returns the manage host of the instance +func (s *AlertmanagerSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *AlertmanagerSpec) IsImported() bool { return s.Imported @@ -117,10 +125,10 @@ func (c *AlertManagerComponent) Instances() []Instance { s.DataDir, }, StatusFn: func(_ context.Context, timeout time.Duration, _ *tls.Config, _ ...string) string { - return statusByHost(s.Host, s.WebPort, "/-/ready", timeout, nil) + return statusByHost(s.GetManageHost(), s.WebPort, "/-/ready", timeout, nil) }, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.WebPort, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.WebPort, timeout, tlsCfg) }, }, topo: c.Topology, diff --git a/pkg/cluster/spec/cdc.go b/pkg/cluster/spec/cdc.go index b54d0ee410..e550b38aa1 100644 --- a/pkg/cluster/spec/cdc.go +++ b/pkg/cluster/spec/cdc.go @@ -34,7 +34,7 @@ import ( // CDCSpec represents the CDC topology specification in topology.yaml type CDCSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -73,6 +73,14 @@ func (s *CDCSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *CDCSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *CDCSpec) IsImported() bool { return s.Imported @@ -116,10 +124,10 @@ func (c *CDCComponent) Instances() []Instance { s.DeployDir, }, StatusFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config, _ ...string) string { - return statusByHost(s.Host, s.Port, "/status", timeout, tlsCfg) + return statusByHost(s.GetManageHost(), s.Port, "/status", timeout, tlsCfg) }, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, c.Topology} if s.DataDir != "" { @@ -190,7 +198,7 @@ func (i *CDCInstance) InitConfig( } cfg := &scripts.CDCScript{ Addr: utils.JoinHostPort(i.GetListenHost(), spec.Port), - AdvertiseAddr: i.GetAddr(), + AdvertiseAddr: utils.JoinHostPort(i.GetHost(), i.GetPort()), PD: strings.Join(pds, ","), GCTTL: spec.GCTTL, TZ: spec.TZ, @@ -261,7 +269,7 @@ func (i *CDCInstance) PreRestart(ctx context.Context, topo Topology, apiTimeoutS } start := time.Now() - client := api.NewCDCOpenAPIClient(ctx, topo.(*Specification).GetCDCList(), 5*time.Second, tlsCfg) + client := api.NewCDCOpenAPIClient(ctx, topo.(*Specification).GetCDCListWithManageHost(), 5*time.Second, tlsCfg) if err := client.Healthy(); err != nil { logger.Debugf("cdc pre-restart skipped, the cluster unhealthy, trigger hard restart, "+ "addr: %s, err: %+v, elapsed: %+v", address, err, time.Since(start)) @@ -328,7 +336,7 @@ func (i *CDCInstance) PostRestart(ctx context.Context, topo Topology, tlsCfg *tl start := time.Now() address := i.GetAddr() - client := api.NewCDCOpenAPIClient(ctx, []string{address}, 5*time.Second, tlsCfg) + client := api.NewCDCOpenAPIClient(ctx, []string{utils.JoinHostPort(i.GetManageHost(), i.GetPort())}, 5*time.Second, tlsCfg) err := client.IsCaptureAlive() if err != nil { logger.Debugf("cdc post-restart finished, get capture status failed, addr: %s, err: %+v, elapsed: %+v", address, err, time.Since(start)) diff --git a/pkg/cluster/spec/dashboard.go b/pkg/cluster/spec/dashboard.go index e415227164..8b686a843b 100644 --- a/pkg/cluster/spec/dashboard.go +++ b/pkg/cluster/spec/dashboard.go @@ -29,7 +29,7 @@ import ( // DashboardSpec represents the Dashboard topology specification in topology.yaml type DashboardSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Version string `yaml:"version,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -51,7 +51,7 @@ func (s *DashboardSpec) Status(ctx context.Context, timeout time.Duration, tlsCf timeout = statusQueryTimeout } - state := statusByHost(s.Host, s.Port, "/status", timeout, tlsCfg) + state := statusByHost(s.GetManageHost(), s.Port, "/status", timeout, tlsCfg) return state } @@ -75,6 +75,14 @@ func (s *DashboardSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *DashboardSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *DashboardSpec) IsImported() bool { // TiDB-Ansible do not support dashboard @@ -121,7 +129,7 @@ func (c *DashboardComponent) Instances() []Instance { }, StatusFn: s.Status, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, c.Topology}) } diff --git a/pkg/cluster/spec/drainer.go b/pkg/cluster/spec/drainer.go index 347e2fd7c0..6327d074e6 100644 --- a/pkg/cluster/spec/drainer.go +++ b/pkg/cluster/spec/drainer.go @@ -32,7 +32,7 @@ import ( // DrainerSpec represents the Drainer topology specification in topology.yaml type DrainerSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -56,7 +56,7 @@ func (s *DrainerSpec) Status(ctx context.Context, timeout time.Duration, tlsCfg timeout = statusQueryTimeout } - state := statusByHost(s.Host, s.Port, "/status", timeout, tlsCfg) + state := statusByHost(s.GetManageHost(), s.Port, "/status", timeout, tlsCfg) if s.Offline { binlogClient, err := api.NewBinlogClient(pdList, timeout, tlsCfg) @@ -94,6 +94,14 @@ func (s *DrainerSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *DrainerSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *DrainerSpec) IsImported() bool { return s.Imported @@ -139,7 +147,7 @@ func (c *DrainerComponent) Instances() []Instance { }, StatusFn: s.Status, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, c.Topology}) } diff --git a/pkg/cluster/spec/grafana.go b/pkg/cluster/spec/grafana.go index 7284bf1d54..6379b9a51e 100644 --- a/pkg/cluster/spec/grafana.go +++ b/pkg/cluster/spec/grafana.go @@ -35,7 +35,7 @@ import ( // GrafanaSpec represents the Grafana topology specification in topology.yaml type GrafanaSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -76,6 +76,14 @@ func (s *GrafanaSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *GrafanaSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *GrafanaSpec) IsImported() bool { return s.Imported @@ -122,10 +130,10 @@ func (c *GrafanaComponent) Instances() []Instance { s.DeployDir, }, StatusFn: func(_ context.Context, timeout time.Duration, _ *tls.Config, _ ...string) string { - return statusByHost(s.Host, s.Port, "/login", timeout, nil) + return statusByHost(s.GetManageHost(), s.Port, "/login", timeout, nil) }, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, topo: c.Topology, diff --git a/pkg/cluster/spec/monitoring.go b/pkg/cluster/spec/monitoring.go index 3698d1d998..19b7b3220b 100644 --- a/pkg/cluster/spec/monitoring.go +++ b/pkg/cluster/spec/monitoring.go @@ -38,7 +38,7 @@ import ( // PrometheusSpec represents the Prometheus Server topology specification in topology.yaml type PrometheusSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -93,6 +93,14 @@ func (s *PrometheusSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *PrometheusSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *PrometheusSpec) IsImported() bool { return s.Imported @@ -139,10 +147,10 @@ func (c *MonitorComponent) Instances() []Instance { s.DataDir, }, StatusFn: func(_ context.Context, timeout time.Duration, _ *tls.Config, _ ...string) string { - return statusByHost(s.Host, s.Port, "/-/ready", timeout, nil) + return statusByHost(s.GetManageHost(), s.Port, "/-/ready", timeout, nil) }, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, c.Topology} if s.NgPort > 0 { diff --git a/pkg/cluster/spec/pd.go b/pkg/cluster/spec/pd.go index 965bdeb973..9134d3b389 100644 --- a/pkg/cluster/spec/pd.go +++ b/pkg/cluster/spec/pd.go @@ -33,7 +33,7 @@ import ( // PDSpec represents the PD topology specification in topology.yaml type PDSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` ListenHost string `yaml:"listen_host,omitempty"` AdvertiseClientAddr string `yaml:"advertise_client_addr,omitempty"` AdvertisePeerAddr string `yaml:"advertise_peer_addr,omitempty"` @@ -61,7 +61,7 @@ func (s *PDSpec) Status(ctx context.Context, timeout time.Duration, tlsCfg *tls. timeout = statusQueryTimeout } - addr := utils.JoinHostPort(s.Host, s.ClientPort) + addr := utils.JoinHostPort(s.GetManageHost(), s.ClientPort) pc := api.NewPDClient(ctx, []string{addr}, timeout, tlsCfg) // check health @@ -101,6 +101,14 @@ func (s *PDSpec) GetMainPort() int { return s.ClientPort } +// GetManageHost returns the manage host of the instance +func (s *PDSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *PDSpec) IsImported() bool { return s.Imported @@ -168,7 +176,7 @@ func (c *PDComponent) Instances() []Instance { }, StatusFn: s.Status, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.ClientPort, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.ClientPort, timeout, tlsCfg) }, }, topo: c.Topology, @@ -372,7 +380,7 @@ func (i *PDInstance) IsLeader(ctx context.Context, topo Topology, apiTimeoutSeco if !ok { panic("topo should be type of tidb topology") } - pdClient := api.NewPDClient(ctx, tidbTopo.GetPDList(), time.Second*5, tlsCfg) + pdClient := api.NewPDClient(ctx, tidbTopo.GetPDListWithManageHost(), time.Second*5, tlsCfg) return i.checkLeader(pdClient) } @@ -397,7 +405,7 @@ func (i *PDInstance) PreRestart(ctx context.Context, topo Topology, apiTimeoutSe if !ok { panic("topo should be type of tidb topology") } - pdClient := api.NewPDClient(ctx, tidbTopo.GetPDList(), time.Second*5, tlsCfg) + pdClient := api.NewPDClient(ctx, tidbTopo.GetPDListWithManageHost(), time.Second*5, tlsCfg) isLeader, err := i.checkLeader(pdClient) if err != nil { @@ -422,7 +430,7 @@ func (i *PDInstance) PostRestart(ctx context.Context, topo Topology, tlsCfg *tls Delay: time.Second, Timeout: 120 * time.Second, } - currentPDAddrs := []string{utils.JoinHostPort(i.Host, i.Port)} + currentPDAddrs := []string{utils.JoinHostPort(i.GetManageHost(), i.Port)} pdClient := api.NewPDClient(ctx, currentPDAddrs, 5*time.Second, tlsCfg) if err := utils.Retry(pdClient.CheckHealth, timeoutOpt); err != nil { diff --git a/pkg/cluster/spec/pump.go b/pkg/cluster/spec/pump.go index 1844403fc7..3431e02ab1 100644 --- a/pkg/cluster/spec/pump.go +++ b/pkg/cluster/spec/pump.go @@ -32,7 +32,7 @@ import ( // PumpSpec represents the Pump topology specification in topology.yaml type PumpSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -55,7 +55,7 @@ func (s *PumpSpec) Status(ctx context.Context, timeout time.Duration, tlsCfg *tl timeout = statusQueryTimeout } - state := statusByHost(s.Host, s.Port, "/status", timeout, tlsCfg) + state := statusByHost(s.GetManageHost(), s.Port, "/status", timeout, tlsCfg) if s.Offline { binlogClient, err := api.NewBinlogClient(pdList, timeout, tlsCfg) @@ -93,6 +93,14 @@ func (s *PumpSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *PumpSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *PumpSpec) IsImported() bool { return s.Imported @@ -138,7 +146,7 @@ func (c *PumpComponent) Instances() []Instance { }, StatusFn: s.Status, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, c.Topology}) } diff --git a/pkg/cluster/spec/spec.go b/pkg/cluster/spec/spec.go index 9c103ea1dc..058c694101 100644 --- a/pkg/cluster/spec/spec.go +++ b/pkg/cluster/spec/spec.go @@ -254,7 +254,7 @@ func (s *Specification) BaseTopo() *BaseTopo { return &BaseTopo{ GlobalOptions: &s.GlobalOptions, MonitoredOptions: s.GetMonitoredOptions(), - MasterList: s.GetPDList(), + MasterList: s.GetPDListWithManageHost(), Monitors: s.Monitors, Grafanas: s.Grafanas, Alertmanagers: s.Alertmanagers, @@ -413,11 +413,26 @@ func (s *Specification) GetPDList() []string { return pdList } -// GetCDCList returns a list of CDC API hosts of the current cluster -func (s *Specification) GetCDCList() []string { +// GetPDListWithManageHost returns a list of PD API hosts of the current cluster +func (s *Specification) GetPDListWithManageHost() []string { + var pdList []string + + for _, pd := range s.PDServers { + pdList = append(pdList, utils.JoinHostPort(pd.GetManageHost(), pd.ClientPort)) + } + + return pdList +} + +// GetCDCListWithManageHost returns a list of CDC API hosts of the current cluster +func (s *Specification) GetCDCListWithManageHost() []string { var result []string for _, server := range s.CDCServers { - result = append(result, utils.JoinHostPort(server.Host, server.Port)) + host := server.Host + if server.ManageHost != "" { + host = server.ManageHost + } + result = append(result, utils.JoinHostPort(host, server.Port)) } return result } @@ -460,14 +475,14 @@ func (s *Specification) GetDashboardAddress(ctx context.Context, tlsCfg *tls.Con // GetEtcdClient loads EtcdClient of current cluster func (s *Specification) GetEtcdClient(tlsCfg *tls.Config) (*clientv3.Client, error) { return clientv3.New(clientv3.Config{ - Endpoints: s.GetPDList(), + Endpoints: s.GetPDListWithManageHost(), TLS: tlsCfg, }) } // GetEtcdProxyClient loads EtcdClient of current cluster with TCP proxy func (s *Specification) GetEtcdProxyClient(tlsCfg *tls.Config, tcpProxy *proxy.TCPProxy) (*clientv3.Client, chan struct{}, error) { - closeC := tcpProxy.Run(s.GetPDList()) + closeC := tcpProxy.Run(s.GetPDListWithManageHost()) cli, err := clientv3.New(clientv3.Config{ Endpoints: tcpProxy.GetEndpoints(), TLS: tlsCfg, diff --git a/pkg/cluster/spec/tidb.go b/pkg/cluster/spec/tidb.go index 45f00b5c9c..ae89dc5069 100644 --- a/pkg/cluster/spec/tidb.go +++ b/pkg/cluster/spec/tidb.go @@ -32,7 +32,7 @@ import ( // TiDBSpec represents the TiDB topology specification in topology.yaml type TiDBSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` ListenHost string `yaml:"listen_host,omitempty"` AdvertiseAddr string `yaml:"advertise_address,omitempty"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` @@ -70,6 +70,14 @@ func (s *TiDBSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *TiDBSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *TiDBSpec) IsImported() bool { return s.Imported @@ -115,10 +123,10 @@ func (c *TiDBComponent) Instances() []Instance { s.DeployDir, }, StatusFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config, _ ...string) string { - return statusByHost(s.Host, s.StatusPort, "/status", timeout, tlsCfg) + return statusByHost(s.GetManageHost(), s.StatusPort, "/status", timeout, tlsCfg) }, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.StatusPort, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.StatusPort, timeout, tlsCfg) }, }, c.Topology}) } diff --git a/pkg/cluster/spec/tiflash.go b/pkg/cluster/spec/tiflash.go index 41a05f88b2..8f6aa19b06 100644 --- a/pkg/cluster/spec/tiflash.go +++ b/pkg/cluster/spec/tiflash.go @@ -41,7 +41,7 @@ import ( // TiFlashSpec represents the TiFlash topology specification in topology.yaml type TiFlashSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -143,6 +143,14 @@ func (s *TiFlashSpec) GetMainPort() int { return s.TCPPort } +// GetManageHost returns the manage host of the instance +func (s *TiFlashSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *TiFlashSpec) IsImported() bool { return s.Imported @@ -291,7 +299,7 @@ func (c *TiFlashComponent) Instances() []Instance { }, StatusFn: s.Status, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.StatusPort, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.StatusPort, timeout, tlsCfg) }, }, c.Topology}) } @@ -478,6 +486,11 @@ func (i *TiFlashInstance) initTiFlashConfig(ctx context.Context, clusterVersion httpPort = fmt.Sprintf(`http_port: %d`, spec.HTTPPort) } } + tcpPort := "#" + // Config tcp_port is only required for TiFlash version < 7.1.0, and is recommended to not specify for TiFlash version >= 7.1.0. + if tidbver.TiFlashRequiresTCPPortConfig(clusterVersion) { + tcpPort = fmt.Sprintf(`tcp_port: %d`, spec.TCPPort) + } // set TLS configs spec.Config, err = i.setTLSConfig(ctx, enableTLS, spec.Config, paths) @@ -503,7 +516,7 @@ server_configs: listen_host: "%[7]s" tmp_path: "%[11]s" %[1]s - tcp_port: %[3]d + %[3]s %[4]s flash.tidb_status_addr: "%[5]s" flash.service_addr: "%[6]s" @@ -527,13 +540,13 @@ server_configs: `, pathConfig, paths.Log, - spec.TCPPort, + tcpPort, httpPort, strings.Join(tidbStatusAddrs, ","), utils.JoinHostPort(spec.Host, spec.FlashServicePort), i.GetListenHost(), spec.StatusPort, - strings.Join(i.getEndpoints(i.topo), ","), + strings.Join(i.topo.(*Specification).GetPDList(), ","), paths.Deploy, fmt.Sprintf("%s/tmp", paths.Data[0]), deprecatedUsersConfig, @@ -837,14 +850,6 @@ type replicateConfig struct { EnablePlacementRules string `json:"enable-placement-rules"` } -func (i *TiFlashInstance) getEndpoints(topo Topology) []string { - var endpoints []string - for _, pd := range topo.(*Specification).PDServers { - endpoints = append(endpoints, utils.JoinHostPort(pd.Host, pd.ClientPort)) - } - return endpoints -} - // PrepareStart checks TiFlash requirements before starting func (i *TiFlashInstance) PrepareStart(ctx context.Context, tlsCfg *tls.Config) error { // set enable-placement-rules to true via PDClient @@ -867,7 +872,7 @@ func (i *TiFlashInstance) PrepareStart(ctx context.Context, tlsCfg *tls.Config) topo = i.topo } - endpoints := i.getEndpoints(topo) + endpoints := topo.(*Specification).GetPDListWithManageHost() pdClient := api.NewPDClient(ctx, endpoints, 10*time.Second, tlsCfg) return pdClient.UpdateReplicateConfig(bytes.NewBuffer(enablePlacementRules)) } @@ -884,7 +889,7 @@ func (i *TiFlashInstance) Ready(ctx context.Context, e ctxt.Executor, timeout ui if i.topo.BaseTopo().GlobalOptions.TLSEnabled { scheme = "https" } - addr := fmt.Sprintf("%s://%s/tiflash/store-status", scheme, utils.JoinHostPort(i.Host, i.GetStatusPort())) + addr := fmt.Sprintf("%s://%s/tiflash/store-status", scheme, utils.JoinHostPort(i.GetManageHost(), i.GetStatusPort())) req, err := http.NewRequest("GET", addr, nil) if err != nil { return err diff --git a/pkg/cluster/spec/tikv.go b/pkg/cluster/spec/tikv.go index 6047bc4b9f..94eab2eca5 100644 --- a/pkg/cluster/spec/tikv.go +++ b/pkg/cluster/spec/tikv.go @@ -47,7 +47,7 @@ const ( // TiKVSpec represents the TiKV topology specification in topology.yaml type TiKVSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` ListenHost string `yaml:"listen_host,omitempty"` AdvertiseAddr string `yaml:"advertise_addr,omitempty"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` @@ -115,6 +115,14 @@ func (s *TiKVSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *TiKVSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *TiKVSpec) IsImported() bool { return s.Imported @@ -192,7 +200,7 @@ func (c *TiKVComponent) Instances() []Instance { }, StatusFn: s.Status, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.StatusPort, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.StatusPort, timeout, tlsCfg) }, }, c.Topology, 0}) } @@ -367,7 +375,7 @@ func (i *TiKVInstance) PreRestart(ctx context.Context, topo Topology, apiTimeout return nil } - pdClient := api.NewPDClient(ctx, tidbTopo.GetPDList(), 5*time.Second, tlsCfg) + pdClient := api.NewPDClient(ctx, tidbTopo.GetPDListWithManageHost(), 5*time.Second, tlsCfg) // Make sure there's leader of PD. // Although we evict pd leader when restart pd, @@ -405,7 +413,7 @@ func (i *TiKVInstance) PostRestart(ctx context.Context, topo Topology, tlsCfg *t return nil } - pdClient := api.NewPDClient(ctx, tidbTopo.GetPDList(), 5*time.Second, tlsCfg) + pdClient := api.NewPDClient(ctx, tidbTopo.GetPDListWithManageHost(), 5*time.Second, tlsCfg) // remove store leader evict scheduler after restart if err := pdClient.RemoveStoreEvict(addr(i.InstanceSpec.(*TiKVSpec))); err != nil { @@ -443,7 +451,7 @@ func genLeaderCounter(topo *Specification, tlsCfg *tls.Config) func(string) (int for _, kv := range topo.TiKVServers { kvid := utils.JoinHostPort(kv.Host, kv.Port) if id == kvid { - statusAddress = utils.JoinHostPort(kv.Host, kv.StatusPort) + statusAddress = utils.JoinHostPort(kv.GetManageHost(), kv.StatusPort) break } foundIds = append(foundIds, kvid) diff --git a/pkg/cluster/spec/tikv_cdc.go b/pkg/cluster/spec/tikv_cdc.go index b371bf7375..6bae98b927 100644 --- a/pkg/cluster/spec/tikv_cdc.go +++ b/pkg/cluster/spec/tikv_cdc.go @@ -34,7 +34,7 @@ import ( // TiKVCDCSpec represents the TiKVCDC topology specification in topology.yaml type TiKVCDCSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` Patched bool `yaml:"patched,omitempty"` @@ -72,6 +72,14 @@ func (s *TiKVCDCSpec) GetMainPort() int { return s.Port } +// GetManageHost returns the manage host of the instance +func (s *TiKVCDCSpec) GetManageHost() string { + if s.ManageHost != "" { + return s.ManageHost + } + return s.Host +} + // IsImported returns if the node is imported from TiDB-Ansible func (s *TiKVCDCSpec) IsImported() bool { // TiDB-Ansible do not support TiKV-CDC @@ -116,10 +124,10 @@ func (c *TiKVCDCComponent) Instances() []Instance { s.DeployDir, }, StatusFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config, _ ...string) string { - return statusByHost(s.Host, s.Port, "/status", timeout, tlsCfg) + return statusByHost(s.GetManageHost(), s.Port, "/status", timeout, tlsCfg) }, UptimeFn: func(_ context.Context, timeout time.Duration, tlsCfg *tls.Config) time.Duration { - return UptimeByHost(s.Host, s.Port, timeout, tlsCfg) + return UptimeByHost(s.GetManageHost(), s.Port, timeout, tlsCfg) }, }, c.Topology} if s.DataDir != "" { @@ -226,11 +234,6 @@ func (i *TiKVCDCInstance) setTLSConfig(ctx context.Context, enableTLS bool, conf var _ RollingUpdateInstance = &TiKVCDCInstance{} -// GetAddr return the address of this TiKV-CDC instance -func (i *TiKVCDCInstance) GetAddr() string { - return utils.JoinHostPort(i.GetHost(), i.GetPort()) -} - // PreRestart implements RollingUpdateInstance interface. // All errors are ignored, to trigger hard restart. func (i *TiKVCDCInstance) PreRestart(ctx context.Context, topo Topology, apiTimeoutSeconds int, tlsCfg *tls.Config) error { @@ -244,7 +247,7 @@ func (i *TiKVCDCInstance) PreRestart(ctx context.Context, topo Topology, apiTime panic("logger not found") } - address := i.GetAddr() + address := utils.JoinHostPort(i.GetHost(), i.GetPort()) // cdc rolling upgrade strategy only works if there are more than 2 captures if len(tidbTopo.TiKVCDCServers) <= 1 { logger.Debugf("tikv-cdc pre-restart skipped, only one capture in the topology, addr: %s", address) @@ -252,7 +255,7 @@ func (i *TiKVCDCInstance) PreRestart(ctx context.Context, topo Topology, apiTime } start := time.Now() - client := api.NewTiKVCDCOpenAPIClient(ctx, []string{address}, 5*time.Second, tlsCfg) + client := api.NewTiKVCDCOpenAPIClient(ctx, []string{utils.JoinHostPort(i.GetManageHost(), i.GetPort())}, 5*time.Second, tlsCfg) captures, err := client.GetAllCaptures() if err != nil { logger.Debugf("tikv-cdc pre-restart skipped, cannot get all captures, trigger hard restart, addr: %s, elapsed: %+v", address, time.Since(start)) @@ -303,9 +306,9 @@ func (i *TiKVCDCInstance) PostRestart(ctx context.Context, topo Topology, tlsCfg } start := time.Now() - address := i.GetAddr() + address := utils.JoinHostPort(i.GetHost(), i.GetPort()) - client := api.NewTiKVCDCOpenAPIClient(ctx, []string{address}, 5*time.Second, tlsCfg) + client := api.NewTiKVCDCOpenAPIClient(ctx, []string{utils.JoinHostPort(i.GetManageHost(), i.GetPort())}, 5*time.Second, tlsCfg) err := client.IsCaptureAlive() if err != nil { logger.Debugf("tikv-cdc post-restart finished, get capture status failed, addr: %s, err: %+v, elapsed: %+v", address, err, time.Since(start)) diff --git a/pkg/cluster/spec/tispark.go b/pkg/cluster/spec/tispark.go index 11a447c0e1..bacd9f4b04 100644 --- a/pkg/cluster/spec/tispark.go +++ b/pkg/cluster/spec/tispark.go @@ -37,7 +37,7 @@ import ( // TiSparkMasterSpec is the topology specification for TiSpark master node type TiSparkMasterSpec struct { Host string `yaml:"host"` - ManageHost string `yaml:"manage_host,omitempty"` + ManageHost string `yaml:"manage_host,omitempty" validate:"manage_host:editable"` ListenHost string `yaml:"listen_host,omitempty"` SSHPort int `yaml:"ssh_port,omitempty" validate:"ssh_port:editable"` Imported bool `yaml:"imported,omitempty"` diff --git a/pkg/tidbver/tidbver.go b/pkg/tidbver/tidbver.go index f9807fe494..ff1d263426 100644 --- a/pkg/tidbver/tidbver.go +++ b/pkg/tidbver/tidbver.go @@ -66,6 +66,15 @@ func TiFlashNotNeedHTTPPortConfig(version string) bool { return semver.Compare(version, "v7.1.0") >= 0 || strings.Contains(version, "nightly") } +// TiFlashRequiresTCPPortConfig return if given version of TiFlash requires tcp_port config. +// TiFlash 7.1.0 and later versions won't listen to tpc_port if the config is not given, which is recommended. +// However this config is required for pre-7.1.0 versions because TiFlash will listen to it anyway, +// and we must make sure the port is being configured as specified in the topology file, +// otherwise multiple TiFlash instances will conflict. +func TiFlashRequiresTCPPortConfig(version string) bool { + return semver.Compare(version, "v7.1.0") < 0 +} + // TiFlashNotNeedSomeConfig return if given version of TiFlash do not need some config like runAsDaemon func TiFlashNotNeedSomeConfig(version string) bool { // https://github.com/pingcap/tiup/pull/1673 diff --git a/pkg/tui/progress/display_props.go b/pkg/tui/progress/display_props.go index 48dd5d2462..5e01a62734 100644 --- a/pkg/tui/progress/display_props.go +++ b/pkg/tui/progress/display_props.go @@ -96,6 +96,7 @@ type DisplayProps struct { Prefix string `json:"prefix,omitempty"` Suffix string `json:"suffix,omitempty"` // If `Mode == Done / Error`, Suffix is not printed Mode Mode `json:"mode,omitempty"` + Detail string `json:"detail,omitempty"` } // String implements string diff --git a/pkg/tui/progress/example_single_bar_test.go b/pkg/tui/progress/example_single_bar_test.go index 0c1f66724a..514a91796f 100644 --- a/pkg/tui/progress/example_single_bar_test.go +++ b/pkg/tui/progress/example_single_bar_test.go @@ -1,6 +1,7 @@ package progress_test import ( + "errors" "strconv" "testing" "time" @@ -42,9 +43,44 @@ func ExampleSingleBar() { b.StopRenderLoop() } +func ExampleSingleBar_err() { + b := progress.NewSingleBar("Prefix") + + b.UpdateDisplay(&progress.DisplayProps{ + Prefix: "Prefix", + Suffix: "Suffix", + }) + + n := 3 + + go func() { + time.Sleep(time.Second) + for i := 0; i < n; i++ { + b.UpdateDisplay(&progress.DisplayProps{ + Prefix: "Prefix" + strconv.Itoa(i), + Suffix: "Suffix" + strconv.Itoa(i), + }) + time.Sleep(time.Second) + } + }() + + b.StartRenderLoop() + + time.Sleep(time.Second * time.Duration(n+1)) + + b.UpdateDisplay(&progress.DisplayProps{ + Mode: progress.ModeError, + Prefix: "Prefix", + Detail: errors.New("expected failure").Error(), + }) + + b.StopRenderLoop() +} + func TestExampleOutput(t *testing.T) { if !testing.Verbose() { return } ExampleSingleBar() + ExampleSingleBar_err() } diff --git a/pkg/tui/progress/single_bar.go b/pkg/tui/progress/single_bar.go index 854f27c160..1f5a454c4e 100644 --- a/pkg/tui/progress/single_bar.go +++ b/pkg/tui/progress/single_bar.go @@ -31,7 +31,7 @@ type singleBarCore struct { func (b *singleBarCore) renderDoneOrError(w io.Writer, dp *DisplayProps) { width := int(termSizeWidth.Load()) - var tail string + var tail, detail string var tailColor *color.Color switch dp.Mode { case ModeDone: @@ -51,7 +51,10 @@ func (b *singleBarCore) renderDoneOrError(w io.Writer, dp *DisplayProps) { } else { displayPrefix = runewidth.Truncate(dp.Prefix, width-prefixWidth, "") } - _, _ = fmt.Fprintf(w, "%s ... %s", displayPrefix, tailColor.Sprint(tail)) + if len(dp.Detail) > 0 { + detail = ": " + dp.Detail + } + _, _ = fmt.Fprintf(w, "%s ... %s%s", displayPrefix, tailColor.Sprint(tail), detail) } func (b *singleBarCore) renderSpinner(w io.Writer, dp *DisplayProps) { diff --git a/pkg/version/version.go b/pkg/version/version.go index b12afe666b..9e9fb746f4 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -23,7 +23,7 @@ var ( // TiUPVerMinor is the minor version of TiUP TiUPVerMinor = 12 // TiUPVerPatch is the patch version of TiUP - TiUPVerPatch = 2 + TiUPVerPatch = 4 // TiUPVerName is an alternative name of the version TiUPVerName = "tiup" // GitHash is the current git commit hash diff --git a/tests/tiup/test_tiup.sh b/tests/tiup/test_tiup.sh index f04fc7ffdf..9fb3b51270 100755 --- a/tests/tiup/test_tiup.sh +++ b/tests/tiup/test_tiup.sh @@ -27,6 +27,7 @@ tiup list tiup tiup help tiup install tidb:v5.2.2 +tiup install tidb:v3.0.13 tiup update tidb tiup update tidb --nightly tiup --binary tidb:nightly