github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/config/cdc_v2.go (about) 1 // Copyright 2023 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 "fmt" 18 "net" 19 "net/url" 20 21 dmysql "github.com/go-sql-driver/mysql" 22 "github.com/pingcap/errors" 23 cerror "github.com/pingcap/tiflow/pkg/errors" 24 "github.com/pingcap/tiflow/pkg/security" 25 ) 26 27 // CDCV2 represents config for ticdc v2 28 type CDCV2 struct { 29 // Enable represents if the cdc v2 is enabled or not 30 Enable bool `toml:"enable" json:"enable"` 31 // MetaStoreConfig represents config for new meta store configurations 32 MetaStoreConfig MetaStoreConfiguration `toml:"meta-store" json:"meta-store"` 33 } 34 35 // MetaStoreConfiguration represents config for new meta store configurations 36 type MetaStoreConfiguration struct { 37 // URI is the address of the meta store. 38 // for example: "mysql://127.0.0.1:3306/test" 39 URI string `toml:"uri" json:"uri"` 40 // SSLCA is the path of the CA certificate file. 41 SSLCa string `toml:"ssl-ca" json:"ssl-ca"` 42 SSLCert string `toml:"ssl-cert" json:"ssl-cert"` 43 SSLKey string `toml:"ssl-key" json:"ssl-key"` 44 } 45 46 // ValidateAndAdjust validates the meta store configurations 47 func (c *CDCV2) ValidateAndAdjust() error { 48 if !c.Enable { 49 return nil 50 } 51 if c.MetaStoreConfig.URI == "" { 52 return errors.New("missing meta store uri configuration") 53 } 54 parsedURI, err := url.Parse(c.MetaStoreConfig.URI) 55 if err != nil { 56 return errors.Trace(err) 57 } 58 if !isSupportedScheme(parsedURI.Scheme) { 59 return errors.Errorf("the %s scheme is not supported by meta store", parsedURI.Scheme) 60 } 61 return nil 62 } 63 64 // GenDSN generates a DSN from the given metastore config. 65 func (cfg *MetaStoreConfiguration) GenDSN() (*dmysql.Config, error) { 66 endpoint, err := url.Parse(cfg.URI) 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 tls, err := cfg.getSSLParam() 71 if err != nil { 72 return nil, errors.Trace(err) 73 } 74 username := endpoint.User.Username() 75 if username == "" { 76 username = "root" 77 } 78 password, _ := endpoint.User.Password() 79 80 hostName := endpoint.Hostname() 81 port := endpoint.Port() 82 if port == "" { 83 port = "3306" 84 } 85 86 // This will handle the IPv6 address format. 87 var dsn *dmysql.Config 88 host := net.JoinHostPort(hostName, port) 89 // dsn format of the driver: 90 // [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] 91 dsnStr := fmt.Sprintf("%s:%s@tcp(%s)%s%s", username, password, host, endpoint.Path, tls) 92 if dsn, err = dmysql.ParseDSN(dsnStr); err != nil { 93 return nil, errors.Trace(err) 94 } 95 96 // create test db used for parameter detection 97 // Refer https://github.com/go-sql-driver/mysql#parameters 98 if dsn.Params == nil { 99 dsn.Params = make(map[string]string) 100 } 101 // enable parseTime for time.Time type 102 dsn.Params["parseTime"] = "true" 103 for key, pa := range endpoint.Query() { 104 dsn.Params[key] = pa[0] 105 } 106 return dsn, nil 107 } 108 109 func (cfg *MetaStoreConfiguration) getSSLParam() (string, error) { 110 if len(cfg.SSLCa) == 0 || len(cfg.SSLCert) == 0 || len(cfg.SSLKey) == 0 { 111 return "", nil 112 } 113 credential := security.Credential{ 114 CAPath: cfg.SSLCa, 115 CertPath: cfg.SSLCert, 116 KeyPath: cfg.SSLKey, 117 } 118 tlsCfg, err := credential.ToTLSConfig() 119 if err != nil { 120 return "", errors.Trace(err) 121 } 122 name := "cdc_mysql_tls_meta_store" 123 err = dmysql.RegisterTLSConfig(name, tlsCfg) 124 if err != nil { 125 return "", cerror.ErrMySQLConnectionError.Wrap(err).GenWithStack("fail to open MySQL connection") 126 } 127 return "?tls=" + name, nil 128 } 129 130 // isSupportedScheme returns true if the scheme is compatible with MySQL. 131 func isSupportedScheme(scheme string) bool { 132 return scheme == "mysql" 133 }