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  }