github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/storetest/settings.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package storetest
     5  
     6  import (
     7  	"database/sql"
     8  	"flag"
     9  	"fmt"
    10  	"net/url"
    11  	"os"
    12  	"path"
    13  
    14  	"github.com/go-sql-driver/mysql"
    15  	_ "github.com/go-sql-driver/mysql"
    16  	_ "github.com/lib/pq"
    17  	"github.com/pkg/errors"
    18  
    19  	"github.com/mattermost/mattermost-server/v5/model"
    20  )
    21  
    22  const (
    23  	defaultMysqlDSN      = "mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s&multiStatements=true"
    24  	defaultPostgresqlDSN = "postgres://mmuser:mostest@localhost:5432/mattermost_test?sslmode=disable&connect_timeout=10"
    25  	defaultMysqlRootPWD  = "mostest"
    26  )
    27  
    28  func getEnv(name, defaultValue string) string {
    29  	if value := os.Getenv(name); value != "" {
    30  		return value
    31  	}
    32  	return defaultValue
    33  }
    34  
    35  func log(message string) {
    36  	verbose := false
    37  	if verboseFlag := flag.Lookup("test.v"); verboseFlag != nil {
    38  		verbose = verboseFlag.Value.String() != ""
    39  	}
    40  	if verboseFlag := flag.Lookup("v"); verboseFlag != nil {
    41  		verbose = verboseFlag.Value.String() != ""
    42  	}
    43  
    44  	if verbose {
    45  		fmt.Println(message)
    46  	}
    47  }
    48  
    49  // MySQLSettings returns the database settings to connect to the MySQL unittesting database.
    50  // The database name is generated randomly and must be created before use.
    51  func MySQLSettings() *model.SqlSettings {
    52  	dsn := getEnv("TEST_DATABASE_MYSQL_DSN", defaultMysqlDSN)
    53  	cfg, err := mysql.ParseDSN(dsn)
    54  	if err != nil {
    55  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    56  	}
    57  
    58  	cfg.DBName = "db" + model.NewId()
    59  
    60  	return databaseSettings("mysql", cfg.FormatDSN())
    61  }
    62  
    63  // PostgresSQLSettings returns the database settings to connect to the PostgreSQL unittesting database.
    64  // The database name is generated randomly and must be created before use.
    65  func PostgreSQLSettings() *model.SqlSettings {
    66  	dsn := getEnv("TEST_DATABASE_POSTGRESQL_DSN", defaultPostgresqlDSN)
    67  	dsnUrl, err := url.Parse(dsn)
    68  	if err != nil {
    69  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    70  	}
    71  
    72  	// Generate a random database name
    73  	dsnUrl.Path = "db" + model.NewId()
    74  
    75  	return databaseSettings("postgres", dsnUrl.String())
    76  }
    77  
    78  func mySQLRootDSN(dsn string) string {
    79  	rootPwd := getEnv("TEST_DATABASE_MYSQL_ROOT_PASSWD", defaultMysqlRootPWD)
    80  	cfg, err := mysql.ParseDSN(dsn)
    81  	if err != nil {
    82  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    83  	}
    84  
    85  	cfg.User = "root"
    86  	cfg.Passwd = rootPwd
    87  	cfg.DBName = "mysql"
    88  
    89  	return cfg.FormatDSN()
    90  }
    91  
    92  func postgreSQLRootDSN(dsn string) string {
    93  	dsnUrl, err := url.Parse(dsn)
    94  	if err != nil {
    95  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    96  	}
    97  
    98  	// // Assume the unittesting database has the same password.
    99  	// password := ""
   100  	// if dsnUrl.User != nil {
   101  	// 	password, _ = dsnUrl.User.Password()
   102  	// }
   103  
   104  	// dsnUrl.User = url.UserPassword("", password)
   105  	dsnUrl.Path = "postgres"
   106  
   107  	return dsnUrl.String()
   108  }
   109  
   110  func mySQLDSNDatabase(dsn string) string {
   111  	cfg, err := mysql.ParseDSN(dsn)
   112  	if err != nil {
   113  		panic("failed to parse dsn " + dsn + ": " + err.Error())
   114  	}
   115  
   116  	return cfg.DBName
   117  }
   118  
   119  func postgreSQLDSNDatabase(dsn string) string {
   120  	dsnUrl, err := url.Parse(dsn)
   121  	if err != nil {
   122  		panic("failed to parse dsn " + dsn + ": " + err.Error())
   123  	}
   124  
   125  	return path.Base(dsnUrl.Path)
   126  }
   127  
   128  func databaseSettings(driver, dataSource string) *model.SqlSettings {
   129  	settings := &model.SqlSettings{
   130  		DriverName:                  &driver,
   131  		DataSource:                  &dataSource,
   132  		DataSourceReplicas:          []string{},
   133  		DataSourceSearchReplicas:    []string{},
   134  		MaxIdleConns:                new(int),
   135  		ConnMaxLifetimeMilliseconds: new(int),
   136  		ConnMaxIdleTimeMilliseconds: new(int),
   137  		MaxOpenConns:                new(int),
   138  		Trace:                       model.NewBool(false),
   139  		AtRestEncryptKey:            model.NewString(model.NewRandomString(32)),
   140  		QueryTimeout:                new(int),
   141  	}
   142  	*settings.MaxIdleConns = 10
   143  	*settings.ConnMaxLifetimeMilliseconds = 3600000
   144  	*settings.ConnMaxIdleTimeMilliseconds = 300000
   145  	*settings.MaxOpenConns = 100
   146  	*settings.QueryTimeout = 60
   147  
   148  	return settings
   149  }
   150  
   151  // execAsRoot executes the given sql as root against the testing database
   152  func execAsRoot(settings *model.SqlSettings, sqlCommand string) error {
   153  	var dsn string
   154  	var driver = *settings.DriverName
   155  
   156  	switch driver {
   157  	case model.DATABASE_DRIVER_MYSQL:
   158  		dsn = mySQLRootDSN(*settings.DataSource)
   159  	case model.DATABASE_DRIVER_POSTGRES:
   160  		dsn = postgreSQLRootDSN(*settings.DataSource)
   161  	default:
   162  		return fmt.Errorf("unsupported driver %s", driver)
   163  	}
   164  
   165  	db, err := sql.Open(driver, dsn)
   166  	if err != nil {
   167  		return errors.Wrapf(err, "failed to connect to %s database as root", driver)
   168  	}
   169  	defer db.Close()
   170  	if _, err = db.Exec(sqlCommand); err != nil {
   171  		return errors.Wrapf(err, "failed to execute `%s` against %s database as root", sqlCommand, driver)
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  // MakeSqlSettings creates a randomly named database and returns the corresponding sql settings
   178  func MakeSqlSettings(driver string) *model.SqlSettings {
   179  	var settings *model.SqlSettings
   180  	var dbName string
   181  
   182  	switch driver {
   183  	case model.DATABASE_DRIVER_MYSQL:
   184  		settings = MySQLSettings()
   185  		dbName = mySQLDSNDatabase(*settings.DataSource)
   186  	case model.DATABASE_DRIVER_POSTGRES:
   187  		settings = PostgreSQLSettings()
   188  		dbName = postgreSQLDSNDatabase(*settings.DataSource)
   189  	default:
   190  		panic("unsupported driver " + driver)
   191  	}
   192  
   193  	if err := execAsRoot(settings, "CREATE DATABASE "+dbName); err != nil {
   194  		panic("failed to create temporary database " + dbName + ": " + err.Error())
   195  	}
   196  
   197  	switch driver {
   198  	case model.DATABASE_DRIVER_MYSQL:
   199  		if err := execAsRoot(settings, "GRANT ALL PRIVILEGES ON "+dbName+".* TO 'mmuser'"); err != nil {
   200  			panic("failed to grant mmuser permission to " + dbName + ":" + err.Error())
   201  		}
   202  	case model.DATABASE_DRIVER_POSTGRES:
   203  		if err := execAsRoot(settings, "GRANT ALL PRIVILEGES ON DATABASE \""+dbName+"\" TO mmuser"); err != nil {
   204  			panic("failed to grant mmuser permission to " + dbName + ":" + err.Error())
   205  		}
   206  	default:
   207  		panic("unsupported driver " + driver)
   208  	}
   209  
   210  	log("Created temporary " + driver + " database " + dbName)
   211  
   212  	return settings
   213  }
   214  
   215  func CleanupSqlSettings(settings *model.SqlSettings) {
   216  	var driver = *settings.DriverName
   217  	var dbName string
   218  
   219  	switch driver {
   220  	case model.DATABASE_DRIVER_MYSQL:
   221  		dbName = mySQLDSNDatabase(*settings.DataSource)
   222  	case model.DATABASE_DRIVER_POSTGRES:
   223  		dbName = postgreSQLDSNDatabase(*settings.DataSource)
   224  	default:
   225  		panic("unsupported driver " + driver)
   226  	}
   227  
   228  	if err := execAsRoot(settings, "DROP DATABASE "+dbName); err != nil {
   229  		panic("failed to drop temporary database " + dbName + ": " + err.Error())
   230  	}
   231  
   232  	log("Dropped temporary database " + dbName)
   233  }