MySqlCluster dump based replication - filter databases
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ccbee8e0 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ccbee8e0 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ccbee8e0 Branch: refs/heads/master Commit: ccbee8e0587620bda5d82f5ef6409267e3a9706c Parents: 15038ce Author: Svetoslav Neykov <[email protected]> Authored: Mon Oct 19 15:30:25 2015 +0300 Committer: Svetoslav Neykov <[email protected]> Committed: Mon Oct 19 18:11:46 2015 +0300 ---------------------------------------------------------------------- .../database/mysql/InitSlaveTaskBody.java | 19 ++++++-- .../entity/database/mysql/MySqlCluster.java | 28 +++++++----- .../entity/database/mysql/MySqlClusterImpl.java | 1 - .../entity/database/mysql/mysql_slave.conf | 25 ++++++++--- .../entity/database/VogellaExampleAccess.java | 47 ++++++++++++++++++-- .../mysql/MySqlClusterIntegrationTest.java | 24 ++++++++++ .../database/mysql/MySqlClusterTestHelper.java | 41 +++++++++++++---- .../org/apache/brooklyn/util/GenericTypes.java | 37 +++++++++++++++ .../brooklyn/util/text/StringEscapes.java | 14 +++++- 9 files changed, 200 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/InitSlaveTaskBody.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/InitSlaveTaskBody.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/InitSlaveTaskBody.java index f125024..1c75275 100644 --- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/InitSlaveTaskBody.java +++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/InitSlaveTaskBody.java @@ -19,6 +19,7 @@ package org.apache.brooklyn.entity.database.mysql; import java.text.SimpleDateFormat; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; @@ -50,11 +51,13 @@ import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.os.Os; import org.apache.brooklyn.util.ssh.BashCommands; import org.apache.brooklyn.util.text.Identifiers; +import org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.concurrent.ConcurrentUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.base.Strings; @@ -64,7 +67,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; public class InitSlaveTaskBody implements Runnable { - private static final String SNAPSHOT_DUMP_OPTIONS = "--skip-lock-tables --single-transaction --flush-logs --hex-blob -A"; + private static final String SNAPSHOT_DUMP_OPTIONS = "--skip-lock-tables --single-transaction --flush-logs --hex-blob"; private static final Logger log = LoggerFactory.getLogger(InitSlaveTaskBody.class); @@ -260,7 +263,7 @@ public class InitSlaveTaskBody implements Runnable { private Future<ReplicationSnapshot> createMasterReplicationSnapshot(final MySqlNode master, final String dumpName) { log.info("MySql cluster " + cluster + ": generating new replication snapshot on master node " + master + " with name " + dumpName); - String dumpOptions = SNAPSHOT_DUMP_OPTIONS + " --master-data=2"; + String dumpOptions = SNAPSHOT_DUMP_OPTIONS + " --master-data=2" + getDumpDatabases(master); ImmutableMap<String, String> params = ImmutableMap.of( ExportDumpEffector.PATH.getName(), dumpName, ExportDumpEffector.ADDITIONAL_OPTIONS.getName(), dumpOptions); @@ -284,11 +287,21 @@ public class InitSlaveTaskBody implements Runnable { }); } + private String getDumpDatabases(MySqlNode node) { + // The config will be inherited from the cluster + Collection<String> dumpDbs = node.config().get(MySqlCluster.SLAVE_REPLICATE_DUMP_DB); + if (dumpDbs != null && !dumpDbs.isEmpty()) { + return " --databases " + Joiner.on(' ').join(Iterables.transform(dumpDbs, BashStringEscapes.wrapBash())); + } else { + return " --all-databases"; + } + } + private Future<ReplicationSnapshot> createSlaveReplicationSnapshot(final MySqlNode slave, final String dumpName) { MySqlClusterUtils.executeSqlOnNodeAsync(slave, "STOP SLAVE SQL_THREAD;"); try { log.info("MySql cluster " + cluster + ": generating new replication snapshot on slave node " + slave + " with name " + dumpName); - String dumpOptions = SNAPSHOT_DUMP_OPTIONS; + String dumpOptions = SNAPSHOT_DUMP_OPTIONS + getDumpDatabases(slave); ImmutableMap<String, String> params = ImmutableMap.of( ExportDumpEffector.PATH.getName(), dumpName, ExportDumpEffector.ADDITIONAL_OPTIONS.getName(), dumpOptions); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlCluster.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlCluster.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlCluster.java index 9ea5ffe..67c1e80 100644 --- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlCluster.java +++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlCluster.java @@ -18,6 +18,7 @@ */ package org.apache.brooklyn.entity.database.mysql; +import java.util.Collection; import java.util.List; import org.apache.brooklyn.api.catalog.Catalog; @@ -29,6 +30,7 @@ import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey.StringAt import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.entity.database.DatastoreMixins.HasDatastoreUrl; import org.apache.brooklyn.entity.group.DynamicCluster; +import org.apache.brooklyn.util.GenericTypes; import com.google.common.reflect.TypeToken; @@ -52,18 +54,20 @@ public interface MySqlCluster extends DynamicCluster, HasDatastoreUrl { ConfigKey<String> SLAVE_USERNAME = ConfigKeys.newStringConfigKey( "mysql.slave.username", "The user name slaves will use to connect to the master", "slave"); - ConfigKey<String> SLAVE_REPLICATE_DO_DB = ConfigKeys.newStringConfigKey( - "mysql.slave.replicate_do_db", "Replicate only listed DBs"); - ConfigKey<String> SLAVE_REPLICATE_IGNORE_DB = ConfigKeys.newStringConfigKey( - "mysql.slave.replicate_ignore_db", "Don't replicate listed DBs"); - ConfigKey<String> SLAVE_REPLICATE_DO_TABLE = ConfigKeys.newStringConfigKey( - "mysql.slave.replicate_do_table", "Replicate only listed tables"); - ConfigKey<String> SLAVE_REPLICATE_IGNORE_TABLE = ConfigKeys.newStringConfigKey( - "mysql.slave.replicate_ignore_table", "Don't replicate listed tables"); - ConfigKey<String> SLAVE_REPLICATE_WILD_DO_TABLE = ConfigKeys.newStringConfigKey( - "mysql.slave.replicate_wild_do_table", "Replicate only listed tables, wildcards acepted"); - ConfigKey<String> SLAVE_REPLICATE_WILD_IGNORE_TABLE = ConfigKeys.newStringConfigKey( - "mysql.slave.replicate_wild_ignore_table", "Don't replicate listed tables, wildcards acepted"); + ConfigKey<Collection<String>> SLAVE_REPLICATE_DO_DB = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_do_db", "Replicate only listed DBs. Use together with 'mysql.slave.replicate_dump_db'."); + ConfigKey<Collection<String>> SLAVE_REPLICATE_IGNORE_DB = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_ignore_db", "Don't replicate listed DBs. Use together with 'mysql.slave.replicate_dump_db'."); + ConfigKey<Collection<String>> SLAVE_REPLICATE_DO_TABLE = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_do_table", "Replicate only listed tables. Use together with 'mysql.slave.replicate_dump_db'."); + ConfigKey<Collection<String>> SLAVE_REPLICATE_IGNORE_TABLE = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_ignore_table", "Don't replicate listed tables. Use together with 'mysql.slave.replicate_dump_db'."); + ConfigKey<Collection<String>> SLAVE_REPLICATE_WILD_DO_TABLE = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_wild_do_table", "Replicate only listed tables, wildcards acepted. Use together with 'mysql.slave.replicate_dump_db'."); + ConfigKey<Collection<String>> SLAVE_REPLICATE_WILD_IGNORE_TABLE = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_wild_ignore_table", "Don't replicate listed tables, wildcards acepted. Use together with 'mysql.slave.replicate_dump_db'."); + ConfigKey<Collection<String>> SLAVE_REPLICATE_DUMP_DB = ConfigKeys.newConfigKey(GenericTypes.COLLECTION_STRING, + "mysql.slave.replicate_dump_db", "Databases to pass to the mysqldump command, used for slave initialization"); StringAttributeSensorAndConfigKey SLAVE_PASSWORD = new StringAttributeSensorAndConfigKey( "mysql.slave.password", "The password slaves will use to connect to the master. Will be auto-generated by default."); @SuppressWarnings("serial") http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterImpl.java ---------------------------------------------------------------------- diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterImpl.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterImpl.java index 77b7c7b..23482a2 100644 --- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterImpl.java +++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterImpl.java @@ -64,7 +64,6 @@ import com.google.common.reflect.TypeToken; // https://dev.mysql.com/doc/refman/5.7/en/replication-howto.html -// TODO Filter dump by database/table, currently all tables are replicated // TODO SSL connection between master and slave // TODO Promote slave to master public class MySqlClusterImpl extends DynamicClusterImpl implements MySqlCluster { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf ---------------------------------------------------------------------- diff --git a/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf b/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf index 2e1e945..1c69423 100644 --- a/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf +++ b/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf @@ -22,12 +22,25 @@ relay-log = mysql-slave-${config["mysql.server_id"]}-relay relay-log-recovery = 1 relay-log-info-repository = TABLE relay-log-purge = 1 -[#if !config["mysql.slave.replicate_do_db"]?? ]#[/#if]replicate-do-db = ${config["mysql.slave.replicate_do_db"]!} -[#if !config["mysql.slave.replicate_ignore_db"]?? ]#[/#if]replicate-ignore-db = ${config["mysql.slave.replicate_ignore_db"]!} -[#if !config["mysql.slave.replicate_do_table"]?? ]#[/#if]replicate-do-table = ${config["mysql.slave.replicate_do_table"]!} -[#if !config["mysql.slave.replicate_ignore_table"]?? ]#[/#if]replicate-ignore-table = ${config["mysql.slave.replicate_ignore_table"]!} -[#if !config["mysql.slave.replicate_wild_do_table"]?? ]#[/#if]replicate-wild-do-table = ${config["mysql.slave.replicate_wild_do_table"]!} -[#if !config["mysql.slave.replicate_wild_ignore_table"]??]#[/#if]replicate-wild-ignore-table = ${config["mysql.slave.replicate_wild_ignore_table"]!} +[#list config["mysql.slave.replicate_do_db"]! as db] +replicate-do-db = ${db} +[/#list] + +[#list config["mysql.slave.replicate_do_table"]! as db] +replicate-do-table = ${db} +[/#list] + +[#list config["mysql.slave.replicate_ignore_table"]! as db] +replicate-ignore-table = ${db} +[/#list] + +[#list config["mysql.slave.replicate_wild_do_table"]! as db] +replicate-wild-do-table = ${db} +[/#list] + +[#list config["mysql.slave.replicate_wild_ignore_table"]! as db] +replicate-wild-ignore-table = ${db} +[/#list] # Custom configuration options ${driver.mySqlServerOptionsString} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/test/java/org/apache/brooklyn/entity/database/VogellaExampleAccess.java ---------------------------------------------------------------------- diff --git a/software/database/src/test/java/org/apache/brooklyn/entity/database/VogellaExampleAccess.java b/software/database/src/test/java/org/apache/brooklyn/entity/database/VogellaExampleAccess.java index e1874b9..348f988 100644 --- a/software/database/src/test/java/org/apache/brooklyn/entity/database/VogellaExampleAccess.java +++ b/software/database/src/test/java/org/apache/brooklyn/entity/database/VogellaExampleAccess.java @@ -18,14 +18,23 @@ */ package org.apache.brooklyn.entity.database; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; -import java.sql.*; -import java.util.List; - /** * Basic JDBC Access test Class, based on the Vogella MySQL tutorial * http://www.vogella.de/articles/MySQLJava/article.html @@ -73,9 +82,13 @@ public class VogellaExampleAccess { } public List<List<String>> readDataBase() throws Exception { + return read("SELECT myuser, webpage, datum, summary, COMMENTS from COMMENTS"); + } + + public List<List<String>> read(String sql) throws SQLException { List<List<String>> results = Lists.newArrayList(); // Result set get the result of the SQL query - ResultSet resultSet = statement.executeQuery("SELECT myuser, webpage, datum, summary, COMMENTS from COMMENTS"); + ResultSet resultSet = statement.executeQuery(sql); // ResultSet is initially before the first data set while (resultSet.next()) { List<String> row = Lists.newArrayList(); @@ -107,6 +120,23 @@ public class VogellaExampleAccess { writeResultSet(readDataBase()); preparedStatement.close(); } + public void execute(String cata, String sql, Object... args) throws Exception { + String prevCata = connect.getCatalog(); + if (cata != null) { + connect.setCatalog(cata); + } + PreparedStatement preparedStatement = connect.prepareStatement(sql); + for (int i = 1; i <= args.length; i++) { + preparedStatement.setObject(i, args[i-1]); + } + preparedStatement.executeUpdate(); + + writeResultSet(readDataBase()); + preparedStatement.close(); + if (cata != null) { + connect.setCatalog(prevCata); + } + } // Remove again the insert comment added by modifyDataBase() public void revertDatabase() throws Exception { @@ -146,6 +176,15 @@ public class VogellaExampleAccess { } } + public Set<String> getSchemas() throws SQLException { + ResultSet rs = statement.executeQuery("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA"); + Set<String> dbs = new HashSet<String>(); + while (rs.next()) { + dbs.add(rs.getString(1)); + } + return dbs; + } + // You should always close the statement and connection public void close() throws Exception { if (statement != null) { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterIntegrationTest.java ---------------------------------------------------------------------- diff --git a/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterIntegrationTest.java b/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterIntegrationTest.java index c250843..c93de8c 100644 --- a/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterIntegrationTest.java +++ b/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterIntegrationTest.java @@ -42,6 +42,7 @@ import org.apache.brooklyn.util.ssh.BashCommands; import org.testng.annotations.Test; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; @@ -101,6 +102,29 @@ public class MySqlClusterIntegrationTest extends BrooklynAppLiveTestSupport { } } + @Test(groups="Integration") + public void testReplicationDatabaseFiltering() throws Exception { + try { + Location loc = getLocation(); + EntitySpec<MySqlCluster> clusterSpec = EntitySpec.create(MySqlCluster.class) + .configure(MySqlMaster.MASTER_CREATION_SCRIPT_CONTENTS, MySqlClusterTestHelper.CREATION_SCRIPT) + .configure(MySqlNode.MYSQL_SERVER_CONF, MutableMap.<String, Object>of("skip-name-resolve","")) + .configure(MySqlCluster.SLAVE_REPLICATE_DO_DB, ImmutableList.of("feedback", "items", "mysql")) + .configure(MySqlCluster.SLAVE_REPLICATE_DUMP_DB, ImmutableList.of("feedback", "items", "mysql")); + + MySqlCluster cluster = MySqlClusterTestHelper.initCluster(app, loc, clusterSpec); + MySqlNode master = (MySqlNode) cluster.getAttribute(MySqlCluster.FIRST); + purgeLogs(cluster, master); + + // test dump replication from master + MySqlNode slave = (MySqlNode) Iterables.getOnlyElement(cluster.invoke(MySqlCluster.RESIZE_BY_DELTA, ImmutableMap.of("delta", 1)).getUnchecked()); + assertEquals(cluster.getAttribute(MySqlCluster.REPLICATION_LAST_SLAVE_SNAPSHOT).getEntityId(), master.getId()); + MySqlClusterTestHelper.assertReplication(master, slave, "db_filter_test"); + } finally { + cleanData(); + } + } + private void deleteSnapshot(MySqlCluster cluster) { ReplicationSnapshot replicationSnapshot = cluster.getAttribute(MySqlCluster.REPLICATION_LAST_SLAVE_SNAPSHOT); Entity snapshotEntity = mgmt.getEntityManager().getEntity(replicationSnapshot.getEntityId()); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterTestHelper.java ---------------------------------------------------------------------- diff --git a/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterTestHelper.java b/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterTestHelper.java index 2ee6465..d99e119 100644 --- a/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterTestHelper.java +++ b/software/database/src/test/java/org/apache/brooklyn/entity/database/mysql/MySqlClusterTestHelper.java @@ -19,8 +19,10 @@ package org.apache.brooklyn.entity.database.mysql; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import java.util.List; +import java.util.Set; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; @@ -70,7 +72,20 @@ public class MySqlClusterTestHelper { " PRIMARY KEY (ID)", " );", "", - "INSERT INTO COMMENTS values (default, 'lars', '[email protected]','http://www.vogella.de', '2009-09-14 10:33:11', 'Summary','My first comment' );" + "INSERT INTO COMMENTS values (default, 'lars', '[email protected]','http://www.vogella.de', '2009-09-14 10:33:11', 'Summary','My first comment' );", + "", + "CREATE DATABASE items;", + "GRANT ALL PRIVILEGES ON items.* TO 'sqluser'@'localhost';", + "GRANT ALL PRIVILEGES ON items.* TO 'sqluser'@'%';", + "FLUSH PRIVILEGES;", + "", + "USE items;", + "CREATE TABLE INVENTORY (MYUSER VARCHAR(30) NOT NULL);", + "INSERT INTO INVENTORY values ('lars');", + "", + "CREATE DATABASE db_filter_test;", + "USE db_filter_test;", + "CREATE TABLE FILTERED (id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (ID));" )); public static void test(TestApplication app, Location location) throws Exception { @@ -91,14 +106,13 @@ public class MySqlClusterTestHelper { MySqlCluster cluster = initCluster(app, location, clusterSpec); MySqlNode master = (MySqlNode) cluster.getAttribute(MySqlCluster.FIRST); MySqlNode slave = (MySqlNode) Iterables.find(cluster.getMembers(), Predicates.not(Predicates.<Entity>equalTo(master))); - //TODO test failing import doesn't abort assertEquals(cluster.getMembers().size(), 2); assertEquals(cluster.getAttribute(MySqlCluster.SLAVE_DATASTORE_URL_LIST).size(), 1); assertEquals(cluster.getAttribute(MySqlNode.DATASTORE_URL), master.getAttribute(MySqlNode.DATASTORE_URL)); assertReplication(master, slave); } - public static void assertReplication(MySqlNode master, MySqlNode slave) throws ClassNotFoundException, Exception { + public static void assertReplication(MySqlNode master, MySqlNode slave, String... notReplicatedSchemas) throws ClassNotFoundException, Exception { VogellaExampleAccess masterDb = new VogellaExampleAccess("com.mysql.jdbc.Driver", master.getAttribute(MySqlNode.DATASTORE_URL)); VogellaExampleAccess slaveDb = new VogellaExampleAccess("com.mysql.jdbc.Driver", slave.getAttribute(MySqlNode.DATASTORE_URL)); masterDb.connect(); @@ -106,10 +120,17 @@ public class MySqlClusterTestHelper { assertSlave(masterDb, slaveDb, 1); masterDb.modifyDataBase(); + masterDb.execute("items", "INSERT INTO INVENTORY values (?);", "Test"); assertSlave(masterDb, slaveDb, 2); masterDb.revertDatabase(); + masterDb.execute("items", "delete from INVENTORY where myuser= ?;", "Test"); assertSlave(masterDb, slaveDb, 1); + Set<String> dbSchemas = slaveDb.getSchemas(); + for (String schema : notReplicatedSchemas) { + assertFalse(dbSchemas.contains(schema), "Database " + schema + " exists on slave"); + } + masterDb.close(); slaveDb.close(); @@ -123,22 +144,24 @@ public class MySqlClusterTestHelper { return mysql; } - public static String execSql(MySqlNode node, String cmd) { - return node.invoke(MySqlNode.EXECUTE_SCRIPT, ImmutableMap.of("commands", cmd)).asTask().getUnchecked(); - } - private static void assertSlave(final VogellaExampleAccess masterDb, final VogellaExampleAccess slaveDb, final int recordCnt) throws Exception { Asserts.succeedsEventually(new Runnable() { + private static final String QUERY = "SELECT C.myuser, webpage, datum, summary, COMMENTS from COMMENTS as C INNER JOIN items.INVENTORY as I ON C.MYUSER=I.MYUSER"; @Override public void run() { try { - List<List<String>> masterResult = masterDb.readDataBase(); + List<List<String>> masterResult = masterDb.read(QUERY); assertEquals(masterResult.size(), recordCnt); - assertEquals(masterResult, slaveDb.readDataBase()); + assertEquals(masterResult, slaveDb.read(QUERY)); } catch (Exception e) { throw Exceptions.propagate(e); } } }); } + + public static String execSql(MySqlNode node, String cmd) { + return node.invoke(MySqlNode.EXECUTE_SCRIPT, ImmutableMap.of("commands", cmd)).asTask().getUnchecked(); + } + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/utils/common/src/main/java/org/apache/brooklyn/util/GenericTypes.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/GenericTypes.java b/utils/common/src/main/java/org/apache/brooklyn/util/GenericTypes.java new file mode 100644 index 0000000..52b0fff --- /dev/null +++ b/utils/common/src/main/java/org/apache/brooklyn/util/GenericTypes.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.util; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.reflect.TypeToken; + +@SuppressWarnings("serial") +public class GenericTypes { + public static final TypeToken<Collection<String>> COLLECTION_STRING = new TypeToken<Collection<String>>() {}; + public static final TypeToken<List<String>> LIST_STRING = new TypeToken<List<String>>() {}; + public static final TypeToken<Set<String>> SET_STRING = new TypeToken<Set<String>>() {}; + public static final TypeToken<Collection<Integer>> COLLECTION_INTEGER = new TypeToken<Collection<Integer>>() {}; + public static final TypeToken<Set<Integer>> LIST_INTEGER = new TypeToken<Set<Integer>>() {}; + public static final TypeToken<Set<Integer>> SET_INTEGER = new TypeToken<Set<Integer>>() {}; + public static final TypeToken<Map<String,Object>> MAP_STRING_OBJECT = new TypeToken<Map<String,Object>>() {}; +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ccbee8e0/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java index 276d0fb..b835e12 100644 --- a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java +++ b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java @@ -32,6 +32,8 @@ import org.apache.brooklyn.util.net.URLParamEncoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Function; + public class StringEscapes { private static final Logger log = LoggerFactory.getLogger(StringEscapes.class); @@ -92,7 +94,17 @@ public class StringEscapes { public static class BashStringEscapes { // single quotes don't permit escapes! e.g. echo 'hello \' world' doesn't work; // you must do 'hello '\'' world' (to get "hello ' world") - + + public static class WrapBashFunction implements Function<String, String> { + @Override + public String apply(String input) { + return wrapBash(input); + } + } + public static Function<String, String> wrapBash() { + return new WrapBashFunction(); + } + /** wraps plain text in double quotes escaped for use in bash double-quoting */ public static String wrapBash(String value) { StringBuilder out = new StringBuilder();
