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