package com.zendesk.maxwell; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.List; import java.util.Map; public class MysqlIsolatedServer { public static final Long SERVER_ID = 4321L; private Connection connection; private String baseDir; private int port; private int serverPid; static final Logger LOGGER = LoggerFactory.getLogger(MysqlIsolatedServer.class); public static final TypeReference<Map<String, Object>> MAP_STRING_OBJECT_REF = new TypeReference<Map<String, Object>>() {}; public void boot(String xtraParams) throws IOException, SQLException, InterruptedException { final String dir = System.getProperty("user.dir"); if ( xtraParams == null ) xtraParams = ""; // By default, MySQL doesn't run under root. However, in an environment like Docker, the root user is the // only available user by default. By adding "--user=root" when the root user is used, we can make sure // the tests can continue to run. boolean isRoot = System.getProperty("user.name").equals("root"); String gtidParams = ""; if (MaxwellTestSupport.inGtidMode()) { LOGGER.info("In gtid test mode."); gtidParams = "--gtid-mode=ON " + "--log-slave-updates=ON " + "--enforce-gtid-consistency=true "; } String serverID = ""; if ( !xtraParams.contains("--server_id") ) serverID = "--server_id=" + SERVER_ID; ProcessBuilder pb = new ProcessBuilder( dir + "/src/test/onetimeserver", "--mysql-version=" + this.getVersion(), "--log-slave-updates", "--log-bin=master", "--binlog_format=row", "--innodb_flush_log_at_trx_commit=0", serverID, "--character-set-server=utf8", "--sync_binlog=0", "--default-time-zone=+00:00", "--verbose", isRoot ? "--user=root" : "", gtidParams, xtraParams ); LOGGER.info("booting onetimeserver: " + StringUtils.join(pb.command(), " ")); Process p = pb.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); p.waitFor(); final BufferedReader errReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); new Thread() { @Override public void run() { while (true) { String l = null; try { l = errReader.readLine(); } catch ( IOException e) {}; if (l == null) break; System.err.println(l); } } }.start(); String json = reader.readLine(); String outputFile = null; try { ObjectMapper mapper = new ObjectMapper(); Map<String, Object> output = mapper.readValue(json, MAP_STRING_OBJECT_REF); this.port = (int) output.get("port"); this.serverPid = (int) output.get("server_pid"); outputFile = (String) output.get("output"); } catch ( Exception e ) { LOGGER.error("got exception while parsing " + json, e); throw(e); } resetConnection(); this.connection.createStatement().executeUpdate("GRANT REPLICATION SLAVE on *.* to 'maxwell'@'127.0.0.1' IDENTIFIED BY 'maxwell'"); this.connection.createStatement().executeUpdate("GRANT ALL on *.* to 'maxwell'@'127.0.0.1'"); this.connection.createStatement().executeUpdate("CREATE DATABASE if not exists test"); LOGGER.info("booted at port " + this.port + ", outputting to file " + outputFile); } public void setupSlave(int masterPort) throws SQLException { Connection master = DriverManager.getConnection("jdbc:mysql://127.0.0.1:" + masterPort + "/mysql?useSSL=false", "root", ""); ResultSet rs = master.createStatement().executeQuery("show master status"); if ( !rs.next() ) throw new RuntimeException("could not get master status"); String file = rs.getString("File"); Long position = rs.getLong("Position"); String changeSQL = String.format( "CHANGE MASTER to master_host = '127.0.0.1', master_user='maxwell', master_password='maxwell', " + "master_log_file = '%s', master_log_pos = %d, master_port = %d", file, position, masterPort ); getConnection().createStatement().execute(changeSQL); getConnection().createStatement().execute("START SLAVE"); } public void boot() throws Exception { boot(null); } public void resetConnection() throws SQLException { this.connection = getNewConnection(); } public Connection getNewConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://127.0.0.1:" + port + "/mysql?zeroDateTimeBehavior=convertToNull&useSSL=false", "root", ""); } public Connection getConnection() { return connection; } public Connection getConnection(String defaultDB) throws SQLException { Connection conn = getNewConnection(); conn.setCatalog(defaultDB); return conn; } public void execute(String query) throws SQLException { getConnection().createStatement().executeUpdate(query); } public void executeList(List<String> queries) throws SQLException { for (String q: queries) { if ( q.matches("^\\s*$") ) continue; execute(q); } } public void executeList(String[] schemaSQL) throws SQLException { executeList(Arrays.asList(schemaSQL)); } public void executeQuery(String sql) throws SQLException { getConnection().createStatement().executeUpdate(sql); } public int getPort() { return port; } public void shutDown() { try { Runtime.getRuntime().exec("kill " + this.serverPid); } catch ( IOException e ) {} } public String getVersion() { String mysqlVersion = System.getenv("MYSQL_VERSION"); return mysqlVersion == null ? "5.5" : mysqlVersion; } }