github.com/mad-app/mattermost-server@v5.11.1+incompatible/store/storetest/settings.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package storetest
     5  
     6  import (
     7  	"flag"
     8  	"fmt"
     9  	"net/url"
    10  	"os"
    11  	"path"
    12  
    13  	"database/sql"
    14  
    15  	"github.com/go-sql-driver/mysql"
    16  	_ "github.com/go-sql-driver/mysql"
    17  	_ "github.com/lib/pq"
    18  	"github.com/pkg/errors"
    19  
    20  	"github.com/mattermost/mattermost-server/model"
    21  )
    22  
    23  const (
    24  	defaultMysqlDSN      = "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s"
    25  	defaultPostgresqlDSN = "postgres://mmuser:mostest@dockerhost:5432/mattermost_test?sslmode=disable&connect_timeout=10"
    26  	defaultMysqlRootPWD  = "mostest"
    27  )
    28  
    29  func getEnv(name, defaultValue string) string {
    30  	if value := os.Getenv(name); value != "" {
    31  		return value
    32  	} else {
    33  		return defaultValue
    34  	}
    35  }
    36  
    37  func log(message string) {
    38  	verbose := false
    39  	if verboseFlag := flag.Lookup("test.v"); verboseFlag != nil {
    40  		verbose = verboseFlag.Value.String() != ""
    41  	}
    42  	if verboseFlag := flag.Lookup("v"); verboseFlag != nil {
    43  		verbose = verboseFlag.Value.String() != ""
    44  	}
    45  
    46  	if verbose {
    47  		fmt.Println(message)
    48  	}
    49  }
    50  
    51  // MySQLSettings returns the database settings to connect to the MySQL unittesting database.
    52  // The database name is generated randomly and must be created before use.
    53  func MySQLSettings() *model.SqlSettings {
    54  	dsn := getEnv("TEST_DATABASE_MYSQL_DSN", defaultMysqlDSN)
    55  	cfg, err := mysql.ParseDSN(dsn)
    56  	if err != nil {
    57  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    58  	}
    59  
    60  	cfg.DBName = "db" + model.NewId()
    61  
    62  	return databaseSettings("mysql", cfg.FormatDSN())
    63  }
    64  
    65  // PostgresSQLSettings returns the database settings to connect to the PostgreSQL unittesting database.
    66  // The database name is generated randomly and must be created before use.
    67  func PostgreSQLSettings() *model.SqlSettings {
    68  	dsn := getEnv("TEST_DATABASE_POSTGRESQL_DSN", defaultPostgresqlDSN)
    69  	dsnUrl, err := url.Parse(dsn)
    70  	if err != nil {
    71  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    72  	}
    73  
    74  	// Generate a random database name
    75  	dsnUrl.Path = "db" + model.NewId()
    76  
    77  	return databaseSettings("postgres", dsnUrl.String())
    78  }
    79  
    80  func mySQLRootDSN(dsn string) string {
    81  	rootPwd := getEnv("TEST_DATABASE_MYSQL_ROOT_PASSWD", defaultMysqlRootPWD)
    82  	cfg, err := mysql.ParseDSN(dsn)
    83  	if err != nil {
    84  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    85  	}
    86  
    87  	cfg.User = "root"
    88  	cfg.Passwd = rootPwd
    89  	cfg.DBName = "mysql"
    90  
    91  	return cfg.FormatDSN()
    92  }
    93  
    94  func postgreSQLRootDSN(dsn string) string {
    95  	dsnUrl, err := url.Parse(dsn)
    96  	if err != nil {
    97  		panic("failed to parse dsn " + dsn + ": " + err.Error())
    98  	}
    99  
   100  	// // Assume the unittesting database has the same password.
   101  	// password := ""
   102  	// if dsnUrl.User != nil {
   103  	// 	password, _ = dsnUrl.User.Password()
   104  	// }
   105  
   106  	// dsnUrl.User = url.UserPassword("", password)
   107  	dsnUrl.Path = "postgres"
   108  
   109  	return dsnUrl.String()
   110  }
   111  
   112  func mySQLDSNDatabase(dsn string) string {
   113  	cfg, err := mysql.ParseDSN(dsn)
   114  	if err != nil {
   115  		panic("failed to parse dsn " + dsn + ": " + err.Error())
   116  	}
   117  
   118  	return cfg.DBName
   119  }
   120  
   121  func postgreSQLDSNDatabase(dsn string) string {
   122  	dsnUrl, err := url.Parse(dsn)
   123  	if err != nil {
   124  		panic("failed to parse dsn " + dsn + ": " + err.Error())
   125  	}
   126  
   127  	return path.Base(dsnUrl.Path)
   128  }
   129  
   130  func databaseSettings(driver, dataSource string) *model.SqlSettings {
   131  	settings := &model.SqlSettings{
   132  		DriverName:                  &driver,
   133  		DataSource:                  &dataSource,
   134  		DataSourceReplicas:          []string{},
   135  		DataSourceSearchReplicas:    []string{},
   136  		MaxIdleConns:                new(int),
   137  		ConnMaxLifetimeMilliseconds: new(int),
   138  		MaxOpenConns:                new(int),
   139  		Trace:                       model.NewBool(false),
   140  		AtRestEncryptKey:            model.NewString(model.NewRandomString(32)),
   141  		QueryTimeout:                new(int),
   142  	}
   143  	*settings.MaxIdleConns = 10
   144  	*settings.ConnMaxLifetimeMilliseconds = 3600000
   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  }