github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/lightning/config/global.go (about)

     1  // Copyright 2019 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 config
    15  
    16  import (
    17  	"flag"
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"time"
    22  
    23  	"github.com/BurntSushi/toml"
    24  	"github.com/carlmjohnson/flagext"
    25  	"github.com/pingcap/errors"
    26  
    27  	"github.com/pingcap/br/pkg/lightning/log"
    28  	"github.com/pingcap/br/pkg/version/build"
    29  )
    30  
    31  type GlobalLightning struct {
    32  	log.Config
    33  	StatusAddr        string `toml:"status-addr" json:"status-addr"`
    34  	ServerMode        bool   `toml:"server-mode" json:"server-mode"`
    35  	CheckRequirements bool   `toml:"check-requirements" json:"check-requirements"`
    36  
    37  	// The legacy alias for setting "status-addr". The value should always the
    38  	// same as StatusAddr, and will not be published in the JSON encoding.
    39  	PProfPort int `toml:"pprof-port" json:"-"`
    40  }
    41  
    42  type GlobalTiDB struct {
    43  	Host       string `toml:"host" json:"host"`
    44  	Port       int    `toml:"port" json:"port"`
    45  	User       string `toml:"user" json:"user"`
    46  	Psw        string `toml:"password" json:"-"`
    47  	StatusPort int    `toml:"status-port" json:"status-port"`
    48  	PdAddr     string `toml:"pd-addr" json:"pd-addr"`
    49  	LogLevel   string `toml:"log-level" json:"log-level"`
    50  }
    51  
    52  type GlobalMydumper struct {
    53  	SourceDir string `toml:"data-source-dir" json:"data-source-dir"`
    54  	// Deprecated
    55  	NoSchema      bool             `toml:"no-schema" json:"no-schema"`
    56  	Filter        []string         `toml:"filter" json:"filter"`
    57  	IgnoreColumns []*IgnoreColumns `toml:"ignore-columns" json:"ignore-columns"`
    58  }
    59  
    60  type GlobalImporter struct {
    61  	Addr        string `toml:"addr" json:"addr"`
    62  	Backend     string `toml:"backend" json:"backend"`
    63  	SortedKVDir string `toml:"sorted-kv-dir" json:"sorted-kv-dir"`
    64  }
    65  
    66  type GlobalConfig struct {
    67  	App          GlobalLightning   `toml:"lightning" json:"lightning"`
    68  	Checkpoint   GlobalCheckpoint  `toml:"checkpoint" json:"checkpoint"`
    69  	TiDB         GlobalTiDB        `toml:"tidb" json:"tidb"`
    70  	Mydumper     GlobalMydumper    `toml:"mydumper" json:"mydumper"`
    71  	TikvImporter GlobalImporter    `toml:"tikv-importer" json:"tikv-importer"`
    72  	PostRestore  GlobalPostRestore `toml:"post-restore" json:"post-restore"`
    73  	Security     Security          `toml:"security" json:"security"`
    74  
    75  	ConfigFileContent []byte
    76  }
    77  
    78  type GlobalCheckpoint struct {
    79  	Enable bool `toml:"enable" json:"enable"`
    80  }
    81  
    82  type GlobalPostRestore struct {
    83  	Checksum PostOpLevel `toml:"checksum" json:"checksum"`
    84  	Analyze  PostOpLevel `toml:"analyze" json:"analyze"`
    85  }
    86  
    87  func NewGlobalConfig() *GlobalConfig {
    88  	return &GlobalConfig{
    89  		App: GlobalLightning{
    90  			ServerMode:        false,
    91  			CheckRequirements: true,
    92  		},
    93  		Checkpoint: GlobalCheckpoint{
    94  			Enable: true,
    95  		},
    96  		TiDB: GlobalTiDB{
    97  			Host:       "127.0.0.1",
    98  			User:       "root",
    99  			StatusPort: 10080,
   100  			LogLevel:   "error",
   101  		},
   102  		Mydumper: GlobalMydumper{
   103  			Filter: DefaultFilter,
   104  		},
   105  		TikvImporter: GlobalImporter{
   106  			Backend: "",
   107  		},
   108  		PostRestore: GlobalPostRestore{
   109  			Checksum: OpLevelRequired,
   110  			Analyze:  OpLevelOptional,
   111  		},
   112  	}
   113  }
   114  
   115  // Must should be called after LoadGlobalConfig(). If LoadGlobalConfig() returns
   116  // any error, this function will exit the program with an appropriate exit code.
   117  func Must(cfg *GlobalConfig, err error) *GlobalConfig {
   118  	switch errors.Cause(err) {
   119  	case nil:
   120  	case flag.ErrHelp:
   121  		os.Exit(0)
   122  	default:
   123  		fmt.Println("Failed to parse command flags: ", err)
   124  		os.Exit(2)
   125  	}
   126  	return cfg
   127  }
   128  
   129  func timestampLogFileName() string {
   130  	return filepath.Join(os.TempDir(), time.Now().Format("lightning.log.2006-01-02T15.04.05Z0700"))
   131  }
   132  
   133  // LoadGlobalConfig reads the arguments and fills in the GlobalConfig.
   134  func LoadGlobalConfig(args []string, extraFlags func(*flag.FlagSet)) (*GlobalConfig, error) {
   135  	cfg := NewGlobalConfig()
   136  	fs := flag.NewFlagSet("", flag.ContinueOnError)
   137  
   138  	// if both `-c` and `-config` are specified, the last one in the command line will take effect.
   139  	// the default value is assigned immediately after the StringVar() call,
   140  	// so it is fine to not give any default value for `-c`, to keep the `-h` page clean.
   141  	var configFilePath string
   142  	fs.StringVar(&configFilePath, "c", "", "(deprecated alias of -config)")
   143  	fs.StringVar(&configFilePath, "config", "", "tidb-lightning configuration file")
   144  	printVersion := fs.Bool("V", false, "print version of lightning")
   145  
   146  	logLevel := flagext.ChoiceVar(fs, "L", "", `log level: info, debug, warn, error, fatal (default info)`, "", "info", "debug", "warn", "warning", "error", "fatal")
   147  	logFilePath := fs.String("log-file", "", "log file path")
   148  	tidbHost := fs.String("tidb-host", "", "TiDB server host")
   149  	tidbPort := fs.Int("tidb-port", 0, "TiDB server port (default 4000)")
   150  	tidbUser := fs.String("tidb-user", "", "TiDB user name to connect")
   151  	tidbPsw := fs.String("tidb-password", "", "TiDB password to connect")
   152  	tidbStatusPort := fs.Int("tidb-status", 0, "TiDB server status port (default 10080)")
   153  	pdAddr := fs.String("pd-urls", "", "PD endpoint address")
   154  	dataSrcPath := fs.String("d", "", "Directory of the dump to import")
   155  	importerAddr := fs.String("importer", "", "address (host:port) to connect to tikv-importer")
   156  	backend := flagext.ChoiceVar(fs, "backend", "", `delivery backend: local, importer, tidb`, "", "local", "importer", "tidb")
   157  	sortedKVDir := fs.String("sorted-kv-dir", "", "path for KV pairs when local backend enabled")
   158  	enableCheckpoint := fs.Bool("enable-checkpoint", true, "whether to enable checkpoints")
   159  	noSchema := fs.Bool("no-schema", false, "ignore schema files, get schema directly from TiDB instead")
   160  	checksum := flagext.ChoiceVar(fs, "checksum", "", "compare checksum after importing.", "", "required", "optional", "off", "true", "false")
   161  	analyze := flagext.ChoiceVar(fs, "analyze", "", "analyze table after importing", "", "required", "optional", "off", "true", "false")
   162  	checkRequirements := fs.Bool("check-requirements", true, "check cluster version before starting")
   163  	tlsCAPath := fs.String("ca", "", "CA certificate path for TLS connection")
   164  	tlsCertPath := fs.String("cert", "", "certificate path for TLS connection")
   165  	tlsKeyPath := fs.String("key", "", "private key path for TLS connection")
   166  	redactInfoLog := fs.Bool("redact-info-log", false, "whether to redact sensitive info in log")
   167  
   168  	statusAddr := fs.String("status-addr", "", "the Lightning server address")
   169  	serverMode := fs.Bool("server-mode", false, "start Lightning in server mode, wait for multiple tasks instead of starting immediately")
   170  
   171  	var filter []string
   172  	flagext.StringsVar(fs, &filter, "f", "select tables to import")
   173  
   174  	if extraFlags != nil {
   175  		extraFlags(fs)
   176  	}
   177  
   178  	if err := fs.Parse(args); err != nil {
   179  		return nil, errors.Trace(err)
   180  	}
   181  	if *printVersion {
   182  		fmt.Println(build.Info())
   183  		return nil, flag.ErrHelp
   184  	}
   185  
   186  	if len(configFilePath) > 0 {
   187  		data, err := os.ReadFile(configFilePath)
   188  		if err != nil {
   189  			return nil, errors.Annotatef(err, "Cannot read config file `%s`", configFilePath)
   190  		}
   191  		if err = toml.Unmarshal(data, cfg); err != nil {
   192  			return nil, errors.Annotatef(err, "Cannot parse config file `%s`", configFilePath)
   193  		}
   194  		cfg.ConfigFileContent = data
   195  	}
   196  
   197  	if *logLevel != "" {
   198  		cfg.App.Config.Level = *logLevel
   199  	}
   200  	if *logFilePath != "" {
   201  		cfg.App.Config.File = *logFilePath
   202  	}
   203  	// "-" is a special config for log to stdout
   204  	if cfg.App.Config.File == "-" {
   205  		cfg.App.Config.File = ""
   206  	} else if cfg.App.Config.File == "" {
   207  		cfg.App.Config.File = timestampLogFileName()
   208  	}
   209  	if *tidbHost != "" {
   210  		cfg.TiDB.Host = *tidbHost
   211  	}
   212  	if *tidbPort != 0 {
   213  		cfg.TiDB.Port = *tidbPort
   214  	}
   215  	if *tidbStatusPort != 0 {
   216  		cfg.TiDB.StatusPort = *tidbStatusPort
   217  	}
   218  	if *tidbUser != "" {
   219  		cfg.TiDB.User = *tidbUser
   220  	}
   221  	if *tidbPsw != "" {
   222  		cfg.TiDB.Psw = *tidbPsw
   223  	}
   224  	if *pdAddr != "" {
   225  		cfg.TiDB.PdAddr = *pdAddr
   226  	}
   227  	if *dataSrcPath != "" {
   228  		cfg.Mydumper.SourceDir = *dataSrcPath
   229  	}
   230  	if *importerAddr != "" {
   231  		cfg.TikvImporter.Addr = *importerAddr
   232  	}
   233  	if *serverMode {
   234  		cfg.App.ServerMode = true
   235  	}
   236  	if *statusAddr != "" {
   237  		cfg.App.StatusAddr = *statusAddr
   238  	}
   239  	if *backend != "" {
   240  		cfg.TikvImporter.Backend = *backend
   241  	}
   242  	if *sortedKVDir != "" {
   243  		cfg.TikvImporter.SortedKVDir = *sortedKVDir
   244  	}
   245  	if !*enableCheckpoint {
   246  		cfg.Checkpoint.Enable = false
   247  	}
   248  	if *noSchema {
   249  		cfg.Mydumper.NoSchema = true
   250  	}
   251  	if *checksum != "" {
   252  		_ = cfg.PostRestore.Checksum.FromStringValue(*checksum)
   253  	}
   254  	if *analyze != "" {
   255  		_ = cfg.PostRestore.Analyze.FromStringValue(*analyze)
   256  	}
   257  	if cfg.App.StatusAddr == "" && cfg.App.PProfPort != 0 {
   258  		cfg.App.StatusAddr = fmt.Sprintf(":%d", cfg.App.PProfPort)
   259  	}
   260  	if !*checkRequirements {
   261  		cfg.App.CheckRequirements = false
   262  	}
   263  	if *tlsCAPath != "" {
   264  		cfg.Security.CAPath = *tlsCAPath
   265  	}
   266  	if *tlsCertPath != "" {
   267  		cfg.Security.CertPath = *tlsCertPath
   268  	}
   269  	if *tlsKeyPath != "" {
   270  		cfg.Security.KeyPath = *tlsKeyPath
   271  	}
   272  	if *redactInfoLog {
   273  		cfg.Security.RedactInfoLog = *redactInfoLog
   274  	}
   275  	if len(filter) > 0 {
   276  		cfg.Mydumper.Filter = filter
   277  	}
   278  
   279  	if cfg.App.StatusAddr == "" && cfg.App.ServerMode {
   280  		return nil, errors.New("If server-mode is enabled, the status-addr must be a valid listen address")
   281  	}
   282  
   283  	cfg.App.Config.Adjust()
   284  	return cfg, nil
   285  }