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&...&paramN=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  }