github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/cmd/server/server.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "context" 18 "strings" 19 "time" 20 21 "github.com/fatih/color" 22 "github.com/pingcap/errors" 23 "github.com/pingcap/failpoint" 24 "github.com/pingcap/log" 25 ticonfig "github.com/pingcap/tidb/pkg/config" 26 "github.com/pingcap/tiflow/cdc/server" 27 cmdcontext "github.com/pingcap/tiflow/pkg/cmd/context" 28 "github.com/pingcap/tiflow/pkg/cmd/util" 29 "github.com/pingcap/tiflow/pkg/config" 30 cerror "github.com/pingcap/tiflow/pkg/errors" 31 "github.com/pingcap/tiflow/pkg/logutil" 32 "github.com/pingcap/tiflow/pkg/security" 33 ticdcutil "github.com/pingcap/tiflow/pkg/util" 34 "github.com/pingcap/tiflow/pkg/version" 35 "github.com/spf13/cobra" 36 "github.com/spf13/pflag" 37 "go.uber.org/zap" 38 ) 39 40 // options defines flags for the `server` command. 41 type options struct { 42 serverConfig *config.ServerConfig 43 serverPdAddr string 44 serverConfigFilePath string 45 46 // TODO(hi-rustin): Consider using a client construction factory here. 47 caPath string 48 certPath string 49 keyPath string 50 allowedCertCN string 51 } 52 53 // newOptions creates new options for the `server` command. 54 func newOptions() *options { 55 return &options{ 56 serverConfig: config.GetDefaultServerConfig(), 57 } 58 } 59 60 // addFlags receives a *cobra.Command reference and binds 61 // flags related to template printing to it. 62 func (o *options) addFlags(cmd *cobra.Command) { 63 cmd.Flags().StringVar(&o.serverConfig.ClusterID, "cluster-id", "default", "Set cdc cluster id") 64 cmd.Flags().StringVar(&o.serverConfig.Addr, "addr", o.serverConfig.Addr, "Set the listening address") 65 cmd.Flags().StringVar(&o.serverConfig.AdvertiseAddr, "advertise-addr", o.serverConfig.AdvertiseAddr, "Set the advertise listening address for client communication") 66 67 cmd.Flags().StringVar(&o.serverConfig.TZ, "tz", o.serverConfig.TZ, "Specify time zone of TiCDC cluster") 68 cmd.Flags().Int64Var(&o.serverConfig.GcTTL, "gc-ttl", o.serverConfig.GcTTL, "CDC GC safepoint TTL duration, specified in seconds") 69 70 cmd.Flags().StringVar(&o.serverConfig.LogFile, "log-file", o.serverConfig.LogFile, "log file path") 71 cmd.Flags().StringVar(&o.serverConfig.LogLevel, "log-level", o.serverConfig.LogLevel, "log level (etc: debug|info|warn|error)") 72 73 cmd.Flags().StringVar(&o.serverConfig.DataDir, "data-dir", o.serverConfig.DataDir, "the path to the directory used to store TiCDC-generated data") 74 75 cmd.Flags().DurationVar((*time.Duration)(&o.serverConfig.OwnerFlushInterval), "owner-flush-interval", time.Duration(o.serverConfig.OwnerFlushInterval), "owner flushes changefeed status interval") 76 _ = cmd.Flags().MarkHidden("owner-flush-interval") 77 78 cmd.Flags().DurationVar((*time.Duration)(&o.serverConfig.ProcessorFlushInterval), "processor-flush-interval", time.Duration(o.serverConfig.ProcessorFlushInterval), "processor flushes task status interval") 79 _ = cmd.Flags().MarkHidden("processor-flush-interval") 80 81 // 80 is safe on most systems. 82 // sort-dir id deprecate, hidden it. 83 cmd.Flags().StringVar(&o.serverConfig.Sorter.SortDir, "sort-dir", o.serverConfig.Sorter.SortDir, "sorter's temporary file directory") 84 _ = cmd.Flags().MarkHidden("sort-dir") 85 86 cmd.Flags().StringVar(&o.serverPdAddr, "pd", "http://127.0.0.1:2379", "Set the PD endpoints to use. Use ',' to separate multiple PDs") 87 cmd.Flags().StringVar(&o.serverConfigFilePath, "config", "", "Path of the configuration file") 88 89 cmd.Flags().StringVar(&o.caPath, "ca", "", "CA certificate path for TLS connection") 90 cmd.Flags().StringVar(&o.certPath, "cert", "", "Certificate path for TLS connection") 91 cmd.Flags().StringVar(&o.keyPath, "key", "", "Private key path for TLS connection") 92 cmd.Flags().StringVar(&o.allowedCertCN, "cert-allowed-cn", "", "Verify caller's identity (cert Common Name). Use ',' to separate multiple CN") 93 } 94 95 // run runs the server cmd. 96 func (o *options) run(cmd *cobra.Command) error { 97 cancel := util.InitCmd(cmd, &logutil.Config{ 98 File: o.serverConfig.LogFile, 99 Level: o.serverConfig.LogLevel, 100 FileMaxSize: o.serverConfig.Log.File.MaxSize, 101 FileMaxDays: o.serverConfig.Log.File.MaxDays, 102 FileMaxBackups: o.serverConfig.Log.File.MaxBackups, 103 ZapInternalErrOutput: o.serverConfig.Log.InternalErrOutput, 104 }) 105 defer cancel() 106 107 _, err := ticdcutil.GetTimezone(o.serverConfig.TZ) 108 if err != nil { 109 return errors.Annotate(err, "can not load timezone, Please specify the time zone through environment variable `TZ` or command line parameters `--tz`") 110 } 111 112 config.StoreGlobalServerConfig(o.serverConfig) 113 ctx := cmdcontext.GetDefaultContext() 114 115 version.LogVersionInfo("Change Data Capture (CDC)") 116 if ticdcutil.FailpointBuild { 117 for _, path := range failpoint.List() { 118 status, err := failpoint.Status(path) 119 if err != nil { 120 log.Error("fail to get failpoint status", zap.Error(err)) 121 } 122 log.Info("failpoint enabled", zap.String("path", path), zap.String("status", status)) 123 } 124 } 125 126 util.LogHTTPProxies() 127 server.RecordGoRuntimeSettings() 128 server, err := server.New(strings.Split(o.serverPdAddr, ",")) 129 if err != nil { 130 log.Error("create cdc server failed", zap.Error(err)) 131 return errors.Trace(err) 132 } 133 // Drain the server before shutdown. 134 util.InitSignalHandling(server.Drain, cancel) 135 136 // Run TiCDC server. 137 err = server.Run(ctx) 138 if err != nil && errors.Cause(err) != context.Canceled { 139 log.Warn("cdc server exits with error", zap.Error(err)) 140 } else { 141 log.Info("cdc server exits normally") 142 } 143 server.Close() 144 return nil 145 } 146 147 // complete adapts from the command line args and config file to the data required. 148 func (o *options) complete(cmd *cobra.Command) error { 149 o.serverConfig.Security = o.getCredential() 150 151 cfg := config.GetDefaultServerConfig() 152 153 if len(o.serverConfigFilePath) > 0 { 154 // strict decode config file, but ignore debug item 155 if err := util.StrictDecodeFile(o.serverConfigFilePath, "TiCDC server", cfg, config.DebugConfigurationItem); err != nil { 156 return err 157 } 158 159 // User specified sort-dir should not take effect, it's always `/tmp/sorter` 160 // if user try to set sort-dir by config file, warn it. 161 if cfg.Sorter.SortDir != config.DefaultSortDir { 162 cmd.Printf(color.HiYellowString("[WARN] --sort-dir is deprecated in server settings. " + 163 "sort-dir will be set to `{data-dir}/tmp/sorter`. The sort-dir here will be no-op\n")) 164 165 cfg.Sorter.SortDir = config.DefaultSortDir 166 } 167 } 168 169 cmd.Flags().Visit(func(flag *pflag.Flag) { 170 switch flag.Name { 171 case "addr": 172 cfg.Addr = o.serverConfig.Addr 173 case "advertise-addr": 174 cfg.AdvertiseAddr = o.serverConfig.AdvertiseAddr 175 case "tz": 176 cfg.TZ = o.serverConfig.TZ 177 case "gc-ttl": 178 cfg.GcTTL = o.serverConfig.GcTTL 179 case "log-file": 180 cfg.LogFile = o.serverConfig.LogFile 181 case "log-level": 182 cfg.LogLevel = o.serverConfig.LogLevel 183 case "data-dir": 184 cfg.DataDir = o.serverConfig.DataDir 185 case "owner-flush-interval": 186 cfg.OwnerFlushInterval = o.serverConfig.OwnerFlushInterval 187 case "processor-flush-interval": 188 cfg.ProcessorFlushInterval = o.serverConfig.ProcessorFlushInterval 189 case "ca": 190 cfg.Security.CAPath = o.serverConfig.Security.CAPath 191 case "cert": 192 cfg.Security.CertPath = o.serverConfig.Security.CertPath 193 case "key": 194 cfg.Security.KeyPath = o.serverConfig.Security.KeyPath 195 case "cert-allowed-cn": 196 cfg.Security.CertAllowedCN = o.serverConfig.Security.CertAllowedCN 197 case "sort-dir": 198 // user specified sorter dir should not take effect, it's always `/tmp/sorter` 199 // if user try to set sort-dir by flag, warn it. 200 if o.serverConfig.Sorter.SortDir != config.DefaultSortDir { 201 cmd.Printf(color.HiYellowString("[WARN] --sort-dir is deprecated in server settings. " + 202 "sort-dir will be set to `{data-dir}/tmp/sorter`. The sort-dir here will be no-op\n")) 203 } 204 cfg.Sorter.SortDir = config.DefaultSortDir 205 case "cluster-id": 206 cfg.ClusterID = o.serverConfig.ClusterID 207 case "pd", "config": 208 // do nothing 209 default: 210 log.Panic("unknown flag, please report a bug", zap.String("flagName", flag.Name)) 211 } 212 }) 213 214 if err := cfg.ValidateAndAdjust(); err != nil { 215 return errors.Trace(err) 216 } 217 218 if cfg.DataDir == "" { 219 cmd.Printf(color.HiYellowString("[WARN] TiCDC server data-dir is not set. " + 220 "Please use `cdc server --data-dir` to start the cdc server if possible.\n")) 221 } 222 223 o.serverConfig = cfg 224 225 return nil 226 } 227 228 // validate checks that the provided attach options are specified. 229 func (o *options) validate() error { 230 if len(o.serverPdAddr) == 0 { 231 return cerror.ErrInvalidServerOption.GenWithStack("empty PD address") 232 } 233 for _, ep := range strings.Split(o.serverPdAddr, ",") { 234 // NOTICE: The configuration used here is the one that has been completed, 235 // as it may be configured by the configuration file. 236 if err := util.VerifyPdEndpoint(ep, o.serverConfig.Security.IsTLSEnabled()); err != nil { 237 return cerror.WrapError(cerror.ErrInvalidServerOption, err) 238 } 239 } 240 return nil 241 } 242 243 // getCredential returns security credential. 244 func (o *options) getCredential() *security.Credential { 245 var certAllowedCN []string 246 if len(o.allowedCertCN) != 0 { 247 certAllowedCN = strings.Split(o.allowedCertCN, ",") 248 } 249 250 return &security.Credential{ 251 CAPath: o.caPath, 252 CertPath: o.certPath, 253 KeyPath: o.keyPath, 254 CertAllowedCN: certAllowedCN, 255 } 256 } 257 258 // NewCmdServer creates the `server` command. 259 func NewCmdServer() *cobra.Command { 260 o := newOptions() 261 262 command := &cobra.Command{ 263 Use: "server", 264 Short: "Start a TiCDC capture server", 265 Args: cobra.NoArgs, 266 RunE: func(cmd *cobra.Command, args []string) error { 267 err := o.complete(cmd) 268 if err != nil { 269 return err 270 } 271 err = o.validate() 272 if err != nil { 273 return err 274 } 275 err = o.run(cmd) 276 cobra.CheckErr(err) 277 return nil 278 }, 279 } 280 281 patchTiDBConf() 282 o.addFlags(command) 283 284 return command 285 } 286 287 func patchTiDBConf() { 288 ticonfig.UpdateGlobal(func(conf *ticonfig.Config) { 289 // Disable kv client batch send loop introduced by tidb library, which is not used in TiCDC server 290 conf.TiKVClient.MaxBatchSize = 0 291 }) 292 }