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 }