type mysqlTester struct { dbConn *sql.DB dbName string host string user string pass string sslmode string port int optionFile string testDBName string } func init() { dbMain = &mysqlTester{} } func (m *mysqlTester) setup() error { var err error m.dbName = viper.GetString("mysql.dbname") m.host = viper.GetString("mysql.host") m.user = viper.GetString("mysql.user") m.pass = viper.GetString("mysql.pass") m.port = viper.GetInt("mysql.port") m.sslmode = viper.GetString("mysql.sslmode") // Create a randomized db name. m.testDBName = randomize.StableDBName(m.dbName) if err = m.makeOptionFile(); err != nil { return errors.Prefix("couldn't make option file", err) } if err = m.dropTestDB(); err != nil { return errors.Err(err) } if err = m.createTestDB(); err != nil { return errors.Err(err) } dumpCmd := exec.Command("mysqldump", m.defaultsFile(), "--no-data", m.dbName) createCmd := exec.Command("mysql", m.defaultsFile(), "--database", m.testDBName) r, w := io.Pipe() dumpCmd.Stdout = w createCmd.Stdin = newFKeyDestroyer(rgxMySQLkey, r) if err = dumpCmd.Start(); err != nil { return errors.Prefix("failed to start mysqldump command", err) } if err = createCmd.Start(); err != nil { return errors.Prefix("failed to start mysql command", err) } if err = dumpCmd.Wait(); err != nil { fmt.Println(err) return errors.Prefix("failed to wait for mysqldump command", err) } w.Close() // After dumpCmd is done, close the write end of the pipe if err = createCmd.Wait(); err != nil { fmt.Println(err) return errors.Prefix("failed to wait for mysql command", err) } return nil } func (m *mysqlTester) sslMode(mode string) string { switch mode { case "true": return "REQUIRED" case "false": return "DISABLED" default: return "PREFERRED" } } func (m *mysqlTester) defaultsFile() string { return fmt.Sprintf("--defaults-file=%s", m.optionFile) } func (m *mysqlTester) makeOptionFile() error { tmp, err := ioutil.TempFile("", "optionfile") if err != nil { return errors.Prefix("failed to create option file", err) } isTCP := false _, err = os.Stat(m.host) if os.IsNotExist(err) { isTCP = true } else if err != nil { return errors.Prefix("could not stat m.host", err) } fmt.Fprintln(tmp, "[client]") fmt.Fprintf(tmp, "host=%s\n", m.host) fmt.Fprintf(tmp, "port=%d\n", m.port) fmt.Fprintf(tmp, "user=%s\n", m.user) fmt.Fprintf(tmp, "password=%s\n", m.pass) fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) if isTCP { fmt.Fprintln(tmp, "protocol=tcp") } fmt.Fprintln(tmp, "[mysqldump]") fmt.Fprintf(tmp, "host=%s\n", m.host) fmt.Fprintf(tmp, "port=%d\n", m.port) fmt.Fprintf(tmp, "user=%s\n", m.user) fmt.Fprintf(tmp, "password=%s\n", m.pass) fmt.Fprintf(tmp, "ssl-mode=%s\n", m.sslMode(m.sslmode)) if isTCP { fmt.Fprintln(tmp, "protocol=tcp") } m.optionFile = tmp.Name() return tmp.Close() } func (m *mysqlTester) createTestDB() error { sql := fmt.Sprintf("create database %s;", m.testDBName) return m.runCmd(sql, "mysql") } func (m *mysqlTester) dropTestDB() error { sql := fmt.Sprintf("drop database if exists %s;", m.testDBName) return m.runCmd(sql, "mysql") } func (m *mysqlTester) teardown() error { if m.dbConn != nil { m.dbConn.Close() } if err := m.dropTestDB(); err != nil { return errors.Err(err) } return os.Remove(m.optionFile) } func (m *mysqlTester) runCmd(stdin, command string, args ...string) error { args = append([]string{m.defaultsFile()}, args...) cmd := exec.Command(command, args...) cmd.Stdin = strings.NewReader(stdin) stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} cmd.Stdout = stdout cmd.Stderr = stderr if err := cmd.Run(); err != nil { fmt.Println("failed running:", command, args) fmt.Println(stdout.String()) fmt.Println(stderr.String()) return errors.Err(err) } return nil } func (m *mysqlTester) conn() (*sql.DB, error) { if m.dbConn != nil { return m.dbConn, nil } var err error m.dbConn, err = sql.Open("mysql", drivers.MySQLBuildQueryString(m.user, m.pass, m.testDBName, m.host, m.port, m.sslmode)) if err != nil { return nil, err } return m.dbConn, nil }