github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/worker/config.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 worker 15 16 import ( 17 "bytes" 18 _ "embed" 19 "encoding/json" 20 "flag" 21 "fmt" 22 "net" 23 "strings" 24 25 "github.com/BurntSushi/toml" 26 "github.com/pingcap/failpoint" 27 "github.com/pingcap/tiflow/dm/config/security" 28 "github.com/pingcap/tiflow/dm/pkg/log" 29 "github.com/pingcap/tiflow/dm/pkg/terror" 30 "github.com/pingcap/tiflow/dm/pkg/utils" 31 "github.com/pingcap/tiflow/pkg/version" 32 ) 33 34 // SampleConfigFile is sample config file of dm-worker. 35 // 36 //go:embed dm-worker.toml 37 var SampleConfigFile string 38 39 var ( 40 defaultKeepAliveTTL = int64(60) // 1 minute 41 defaultRelayKeepAliveTTL = int64(60 * 30) // 30 minutes 42 ) 43 44 func init() { 45 failpoint.Inject("defaultKeepAliveTTL", func(val failpoint.Value) { 46 i := val.(int) 47 defaultKeepAliveTTL = int64(i) 48 }) 49 failpoint.Inject("defaultRelayKeepAliveTTL", func(val failpoint.Value) { 50 i := val.(int) 51 defaultRelayKeepAliveTTL = int64(i) 52 }) 53 } 54 55 // NewConfig creates a new base config for worker. 56 func NewConfig() *Config { 57 cfg := &Config{} 58 cfg.flagSet = flag.NewFlagSet("worker", flag.ContinueOnError) 59 fs := cfg.flagSet 60 61 fs.BoolVar(&cfg.printVersion, "V", false, "prints version and exit") 62 fs.BoolVar(&cfg.printSampleConfig, "print-sample-config", false, "print sample config file of dm-worker") 63 fs.StringVar(&cfg.ConfigFile, "config", "", "path to config file") 64 fs.StringVar(&cfg.WorkerAddr, "worker-addr", "", "listen address for client traffic") 65 fs.StringVar(&cfg.AdvertiseAddr, "advertise-addr", "", `advertise address for client traffic (default "${worker-addr}")`) 66 fs.StringVar(&cfg.LogLevel, "L", "info", "log level: debug, info, warn, error, fatal") 67 fs.StringVar(&cfg.LogFile, "log-file", "", "log file path") 68 fs.StringVar(&cfg.LogFormat, "log-format", "text", `the format of the log, "text" or "json"`) 69 // fs.StringVar(&cfg.LogRotate, "log-rotate", "day", "log file rotate type, hour/day") 70 // NOTE: add `advertise-addr` for dm-master if needed. 71 fs.StringVar(&cfg.Join, "join", "", `join to an existing cluster (usage: dm-master cluster's "${master-addr}")`) 72 fs.StringVar(&cfg.Name, "name", "", "human-readable name for DM-worker member") 73 fs.Int64Var(&cfg.KeepAliveTTL, "keepalive-ttl", defaultKeepAliveTTL, "dm-worker's TTL for keepalive with etcd (in seconds)") 74 fs.Int64Var(&cfg.RelayKeepAliveTTL, "relay-keepalive-ttl", defaultRelayKeepAliveTTL, "dm-worker's TTL for keepalive with etcd when handle relay enabled sources (in seconds)") 75 76 fs.StringVar(&cfg.SSLCA, "ssl-ca", "", "path of file that contains list of trusted SSL CAs for connection") 77 fs.StringVar(&cfg.SSLCert, "ssl-cert", "", "path of file that contains X509 certificate in PEM format for connection") 78 fs.StringVar(&cfg.SSLKey, "ssl-key", "", "path of file that contains X509 key in PEM format for connection") 79 fs.Var(&cfg.CertAllowedCN, "cert-allowed-cn", "the trusted common name that allowed to visit") 80 81 return cfg 82 } 83 84 // Config is the configuration. 85 type Config struct { 86 flagSet *flag.FlagSet 87 Name string `toml:"name" json:"name"` 88 89 LogLevel string `toml:"log-level" json:"log-level"` 90 LogFile string `toml:"log-file" json:"log-file"` 91 LogFormat string `toml:"log-format" json:"log-format"` 92 LogRotate string `toml:"log-rotate" json:"log-rotate"` 93 94 Join string `toml:"join" json:"join" ` 95 WorkerAddr string `toml:"worker-addr" json:"worker-addr"` 96 AdvertiseAddr string `toml:"advertise-addr" json:"advertise-addr"` 97 98 ConfigFile string `toml:"config-file" json:"config-file"` 99 // TODO: in the future dm-workers should share a same ttl from dm-master 100 KeepAliveTTL int64 `toml:"keepalive-ttl" json:"keepalive-ttl"` 101 RelayKeepAliveTTL int64 `toml:"relay-keepalive-ttl" json:"relay-keepalive-ttl"` 102 103 RelayDir string `toml:"relay-dir" json:"relay-dir"` 104 105 // tls config 106 security.Security 107 108 printVersion bool 109 printSampleConfig bool 110 } 111 112 // Clone clones a config. 113 func (c *Config) Clone() *Config { 114 clone := &Config{} 115 *clone = *c 116 return clone 117 } 118 119 func (c *Config) String() string { 120 cfg, err := json.Marshal(c) 121 if err != nil { 122 log.L().Error("fail to marshal config to json", log.ShortError(err)) 123 } 124 return string(cfg) 125 } 126 127 // Toml returns TOML format representation of config. 128 func (c *Config) Toml() (string, error) { 129 var b bytes.Buffer 130 131 err := toml.NewEncoder(&b).Encode(c) 132 if err != nil { 133 log.L().Error("fail to marshal config to toml", log.ShortError(err)) 134 } 135 136 return b.String(), nil 137 } 138 139 // Parse parses flag definitions from the argument list. 140 func (c *Config) Parse(arguments []string) error { 141 // Parse first to get config file. 142 err := c.flagSet.Parse(arguments) 143 if err != nil { 144 return terror.ErrWorkerParseFlagSet.Delegate(err) 145 } 146 147 if c.printVersion { 148 fmt.Println(version.GetRawInfo()) 149 return flag.ErrHelp 150 } 151 152 if c.printSampleConfig { 153 fmt.Println(SampleConfigFile) 154 return flag.ErrHelp 155 } 156 157 // Load config file if specified. 158 if c.ConfigFile != "" { 159 err = c.configFromFile(c.ConfigFile) 160 if err != nil { 161 return err 162 } 163 } 164 165 // Parse again to replace with command line options. 166 err = c.flagSet.Parse(arguments) 167 if err != nil { 168 return terror.ErrWorkerParseFlagSet.Delegate(err) 169 } 170 171 if len(c.flagSet.Args()) != 0 { 172 return terror.ErrWorkerInvalidFlag.Generate(c.flagSet.Arg(0)) 173 } 174 175 return c.adjust() 176 } 177 178 // adjust adjusts the config. 179 func (c *Config) adjust() error { 180 c.WorkerAddr = utils.UnwrapScheme(c.WorkerAddr) 181 host, _, err := net.SplitHostPort(c.WorkerAddr) 182 if err != nil { 183 return terror.ErrWorkerHostPortNotValid.Delegate(err, c.WorkerAddr) 184 } 185 186 if c.AdvertiseAddr == "" { 187 if host == "" || host == "0.0.0.0" { 188 return terror.ErrWorkerHostPortNotValid.Generatef("worker-addr (%s) must include the 'host' part (should not be '0.0.0.0') when advertise-addr is not set", c.WorkerAddr) 189 } 190 c.AdvertiseAddr = c.WorkerAddr 191 } else { 192 c.AdvertiseAddr = utils.UnwrapScheme(c.AdvertiseAddr) 193 var port string 194 host, port, err = net.SplitHostPort(c.AdvertiseAddr) 195 if err != nil { 196 return terror.ErrWorkerHostPortNotValid.Delegate(err, c.AdvertiseAddr) 197 } 198 if host == "" || host == "0.0.0.0" || len(port) == 0 { 199 return terror.ErrWorkerHostPortNotValid.Generate("advertise-addr (%s) must include the 'host' part and should not be '0.0.0.0'", c.AdvertiseAddr) 200 } 201 } 202 203 if c.Name == "" { 204 fmt.Printf("worker name is not given, we will set AdvertiseAddr %s as the worker name\n", c.AdvertiseAddr) 205 c.Name = c.AdvertiseAddr 206 } 207 208 if c.Join != "" { 209 c.Join = utils.WrapSchemes(c.Join, c.SSLCA != "") 210 } 211 212 return nil 213 } 214 215 // configFromFile loads config from file. 216 func (c *Config) configFromFile(path string) error { 217 metaData, err := toml.DecodeFile(path, c) 218 if err != nil { 219 return terror.ErrWorkerDecodeConfigFromFile.Delegate(err) 220 } 221 undecoded := metaData.Undecoded() 222 if len(undecoded) > 0 && err == nil { 223 var undecodedItems []string 224 for _, item := range undecoded { 225 undecodedItems = append(undecodedItems, item.String()) 226 } 227 return terror.ErrWorkerUndecodedItemFromFile.Generate(strings.Join(undecodedItems, ",")) 228 } 229 return nil 230 }