github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/mysql/config.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package mysql
    21  
    22  import (
    23  	"crypto/tls"
    24  	"crypto/x509"
    25  	"database/sql"
    26  	"fmt"
    27  	"net"
    28  	"os"
    29  	"strconv"
    30  	"time"
    31  
    32  	"github.com/go-sql-driver/mysql"
    33  	"github.com/pkg/errors"
    34  	"github.com/spf13/viper"
    35  
    36  	"github.com/1aal/kubeblocks/pkg/constant"
    37  )
    38  
    39  const (
    40  	// configurations to connect to MySQL, either a data source name represent by URL.
    41  	connectionURLKey = "url"
    42  
    43  	// To connect to MySQL running over SSL you have to download a
    44  	// SSL certificate. If this is provided the driver will connect using
    45  	// SSL. If you have disabled SSL you can leave this empty.
    46  	// When the user provides a pem path their connection string must end with
    47  	// &tls=custom
    48  	// The connection string should be in the following format
    49  	// "%s:%s@tcp(%s:3306)/%s?allowNativePasswords=true&tls=custom",'myadmin@mydemoserver', 'yourpassword', 'mydemoserver.mysql.database.azure.com', 'targetdb'.
    50  	pemPathKey = "pemPath"
    51  
    52  	// other general settings for DB connections.
    53  	maxIdleConnsKey    = "maxIdleConns"
    54  	maxOpenConnsKey    = "maxOpenConns"
    55  	connMaxLifetimeKey = "connMaxLifetime"
    56  	connMaxIdleTimeKey = "connMaxIdleTime"
    57  )
    58  
    59  const (
    60  	databaseName  = "databaseName"
    61  	adminDatabase = "mysql"
    62  	defaultDBPort = 3306
    63  )
    64  
    65  type Config struct {
    66  	url             string
    67  	username        string
    68  	password        string
    69  	pemPath         string
    70  	maxIdleConns    int
    71  	maxOpenConns    int
    72  	connMaxLifetime time.Duration
    73  	connMaxIdletime time.Duration
    74  }
    75  
    76  var config *Config
    77  
    78  func NewConfig(properties map[string]string) (*Config, error) {
    79  	config = &Config{}
    80  
    81  	if val, ok := properties[connectionURLKey]; ok && val != "" {
    82  		config.url = val
    83  	} else {
    84  		config.url = "root:@tcp(127.0.0.1:3306)/mysql?multiStatements=true"
    85  	}
    86  
    87  	if viper.IsSet(constant.KBEnvServiceUser) {
    88  		config.username = viper.GetString(constant.KBEnvServiceUser)
    89  	}
    90  
    91  	if viper.IsSet(constant.KBEnvServicePassword) {
    92  		config.password = viper.GetString(constant.KBEnvServicePassword)
    93  	}
    94  
    95  	if val, ok := properties[pemPathKey]; ok {
    96  		config.pemPath = val
    97  	}
    98  
    99  	if val, ok := properties[maxIdleConnsKey]; ok {
   100  		if i, err := strconv.Atoi(val); err == nil {
   101  			config.maxIdleConns = i
   102  		}
   103  	}
   104  
   105  	if val, ok := properties[maxOpenConnsKey]; ok {
   106  		if i, err := strconv.Atoi(val); err == nil {
   107  			config.maxOpenConns = i
   108  		}
   109  	}
   110  
   111  	if val, ok := properties[connMaxLifetimeKey]; ok {
   112  		if d, err := time.ParseDuration(val); err == nil {
   113  			config.connMaxLifetime = d
   114  		}
   115  	}
   116  
   117  	if val, ok := properties[connMaxIdleTimeKey]; ok {
   118  		if d, err := time.ParseDuration(val); err == nil {
   119  			config.connMaxIdletime = d
   120  		}
   121  	}
   122  
   123  	if config.pemPath != "" {
   124  		rootCertPool := x509.NewCertPool()
   125  		pem, err := os.ReadFile(config.pemPath)
   126  		if err != nil {
   127  			return nil, errors.Wrapf(err, "Error reading PEM file from %s", config.pemPath)
   128  		}
   129  
   130  		ok := rootCertPool.AppendCertsFromPEM(pem)
   131  		if !ok {
   132  			return nil, fmt.Errorf("failed to append PEM")
   133  		}
   134  
   135  		err = mysql.RegisterTLSConfig("custom", &tls.Config{RootCAs: rootCertPool, MinVersion: tls.VersionTLS12})
   136  		if err != nil {
   137  			return nil, errors.Wrap(err, "Error register TLS config")
   138  		}
   139  	}
   140  	return config, nil
   141  }
   142  
   143  func (config *Config) GetLocalDBConn() (*sql.DB, error) {
   144  	mysqlConfig, err := mysql.ParseDSN(config.url)
   145  	if err != nil {
   146  		return nil, errors.Wrapf(err, "illegal Data Source Name (DNS) specified by %s", connectionURLKey)
   147  	}
   148  	mysqlConfig.User = config.username
   149  	mysqlConfig.Passwd = config.password
   150  	db, err := sql.Open("mysql", mysqlConfig.FormatDSN())
   151  	if err != nil {
   152  		return nil, errors.Wrap(err, "error opening DB connection")
   153  	}
   154  
   155  	return db, nil
   156  }
   157  
   158  func (config *Config) GetDBConnWithAddr(addr string) (*sql.DB, error) {
   159  	mysqlConfig, err := mysql.ParseDSN(config.url)
   160  	if err != nil {
   161  		return nil, errors.Wrapf(err, "illegal Data Source Name (DNS) specified by %s", connectionURLKey)
   162  	}
   163  	mysqlConfig.User = config.username
   164  	mysqlConfig.Passwd = config.password
   165  	mysqlConfig.Addr = addr
   166  	db, err := sql.Open("mysql", mysqlConfig.FormatDSN())
   167  	if err != nil {
   168  		return nil, errors.Wrap(err, "error opening DB connection")
   169  	}
   170  
   171  	return db, nil
   172  }
   173  
   174  func (config *Config) GetDBPort() int {
   175  	mysqlConfig, err := mysql.ParseDSN(config.url)
   176  	if err != nil {
   177  		return defaultDBPort
   178  	}
   179  
   180  	_, portStr, err := net.SplitHostPort(mysqlConfig.Addr)
   181  	if err != nil {
   182  		return defaultDBPort
   183  	}
   184  
   185  	port, err := strconv.Atoi(portStr)
   186  	if err != nil {
   187  		return defaultDBPort
   188  	}
   189  
   190  	return port
   191  }
   192  
   193  func GetConfig() *Config {
   194  	return config
   195  }