brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sjcorb...@apache.org
Subject [5/7] incubator-brooklyn git commit: MySqlCluster dump based replication - filter databases
Date Mon, 26 Oct 2015 12:31:26 GMT
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 <svetoslav.neykov@cloudsoftcorp.com>
Authored: Mon Oct 19 15:30:25 2015 +0300
Committer: Svetoslav Neykov <svetoslav.neykov@cloudsoftcorp.com>
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', 'myemail@gmail.com','http://www.vogella.de',
'2009-09-14 10:33:11', 'Summary','My first comment' );"
+            "INSERT INTO COMMENTS values (default, 'lars', 'myemail@gmail.com','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();


Mime
View raw message