/* * 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.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; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.core.test.entity.TestApplication; import org.apache.brooklyn.entity.database.VogellaExampleAccess; import org.apache.brooklyn.entity.database.mysql.MySqlCluster.MySqlMaster; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.exceptions.Exceptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; /** * Runs a slightly modified version of the popular Vogella MySQL tutorial, * from * http://www.vogella.de/articles/MySQLJava/article.html */ public class MySqlClusterTestHelper { public static final Logger log = LoggerFactory.getLogger(MySqlClusterTestHelper.class); // From http://www.vogella.de/articles/MySQLJava/article.html public static final String CREATION_SCRIPT = Joiner.on("\n").join(ImmutableList.of( "CREATE DATABASE feedback;", "CREATE USER 'sqluser'@'localhost' IDENTIFIED BY 'sqluserpw';", "GRANT USAGE ON *.* TO 'sqluser'@'localhost';", "GRANT ALL PRIVILEGES ON feedback.* TO 'sqluser'@'localhost';", "CREATE USER 'sqluser'@'%' IDENTIFIED BY 'sqluserpw';", "GRANT USAGE ON *.* TO 'sqluser'@'%';", "GRANT ALL PRIVILEGES ON feedback.* TO 'sqluser'@'%';", "FLUSH PRIVILEGES;", "USE feedback;", "CREATE TABLE COMMENTS (", " id INT NOT NULL AUTO_INCREMENT,", " MYUSER VARCHAR(30) NOT NULL,", " EMAIL VARCHAR(30), ", " WEBPAGE VARCHAR(100) NOT NULL,", " DATUM DATE NOT NULL, ", " SUMMARY VARCHAR(40) NOT NULL,", " COMMENTS VARCHAR(400) NOT NULL,", " 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' );", "", "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 { test(app, location, EntitySpec.create(MySqlCluster.class) .configure(MySqlCluster.INITIAL_SIZE, 2) .configure(MySqlNode.CREATION_SCRIPT_CONTENTS, CREATION_SCRIPT) .configure(MySqlNode.MYSQL_SERVER_CONF, minimalMemoryConfig())); } public static MutableMap<String, Object> minimalMemoryConfig() { // http://www.tocker.ca/2014/03/10/configuring-mysql-to-use-minimal-memory.html return MutableMap.<String, Object>of() .add("skip-name-resolve","") .add("performance_schema","0") .add("innodb_buffer_pool_size","5M") .add("innodb_log_buffer_size","256K") .add("query_cache_size","0") .add("max_connections","10") .add("key_buffer_size","8") .add("thread_cache_size","0") .add("host_cache_size","0") .add("innodb_ft_cache_size","1600000") .add("innodb_ft_total_cache_size","32000000") // per thread or per operation settings .add("thread_stack","131072") .add("sort_buffer_size","32K") .add("read_buffer_size","8200") .add("read_rnd_buffer_size","8200") .add("max_heap_table_size","16K") .add("tmp_table_size","1K") .add("bulk_insert_buffer_size","0") .add("join_buffer_size","128") .add("net_buffer_length","1K") .add("innodb_sort_buffer_size","64K") // settings that relate to the binary log (if enabled) .add("binlog_cache_size","4K") .add("binlog_stmt_cache_size","4K"); } public static void testMasterInit(TestApplication app, Location location) throws Exception { test(app, location, EntitySpec.create(MySqlCluster.class) .configure(MySqlCluster.INITIAL_SIZE, 2) .configure(MySqlMaster.MASTER_CREATION_SCRIPT_CONTENTS, CREATION_SCRIPT) .configure(MySqlNode.MYSQL_SERVER_CONF, minimalMemoryConfig())); } public static void test(TestApplication app, Location location, EntitySpec<MySqlCluster> clusterSpec) throws Exception { 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))); 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, 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(); slaveDb.connect(); 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(); log.info("Ran vogella MySQL example -- SUCCESS"); } public static MySqlCluster initCluster(TestApplication app, Location location, EntitySpec<MySqlCluster> spec) { MySqlCluster mysql = app.createAndManageChild(spec); app.start(ImmutableList.of(location)); log.info("MySQL started"); return mysql; } 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.read(QUERY); assertEquals(masterResult.size(), recordCnt); 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(); } }