diff --git a/.travis.yml b/.travis.yml
index 8c29aff..b9b7149 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,9 +10,9 @@ matrix:
- jdk: oraclejdk7
script: mvn test
- jdk: oraclejdk8
- script:
- - mvn test
- - mvn verify
+ script: mvn test
+ - jdk: oraclejdk8
+ script: mvn verify
deploy:
provider: script
script: ./scripts/deploy.sh
diff --git a/README.md b/README.md
index 32169e8..c716962 100644
--- a/README.md
+++ b/README.md
@@ -199,7 +199,7 @@ https://github.com/builtamont/cassandra-migration/releases
## Version 0.9 Release Pending Actions
* Replace `config.Cluster.java` and `config.Keyspace.java` to the one provided by DataStax Cassandra driver
- * Add additional features from upstream open PRs
+ * ~~Add additional features from upstream open PRs~~ (DONE as per 8 September 2016 PRs)
* Add standalone Cassandra (DataStax Community Edition) integration test
## Non-Critical Pending Actions
diff --git a/pom.xml b/pom.xml
index bedc6ba..67126e2 100755
--- a/pom.xml
+++ b/pom.xml
@@ -197,9 +197,9 @@
2.19.1
- 3
+ 1
true
- -Xmx1024m -XX:MaxPermSize=256m
+ -Xmx2048m -XX:MaxPermSize=512m
diff --git a/src/main/java/com/builtamont/cassandra/migration/CassandraMigration.kt b/src/main/java/com/builtamont/cassandra/migration/CassandraMigration.kt
index 6eca77d..3acaf08 100644
--- a/src/main/java/com/builtamont/cassandra/migration/CassandraMigration.kt
+++ b/src/main/java/com/builtamont/cassandra/migration/CassandraMigration.kt
@@ -38,7 +38,6 @@ import com.builtamont.cassandra.migration.internal.util.logging.LogFactory
import com.datastax.driver.core.Cluster
import com.datastax.driver.core.Metadata
import com.datastax.driver.core.Session
-import sun.reflect.generics.reflectiveObjects.NotImplementedException
/**
* This is the centre point of Cassandra migration, and for most users, the only class they will ever have to deal with.
@@ -87,24 +86,18 @@ class CassandraMigration : CassandraMigrationConfiguration {
* @return The number of successfully applied migrations.
*/
fun migrate(): Int {
- return execute(object : Action {
- override fun execute(session: Session): Int {
- Initialize().run(session, keyspace, MigrationVersion.CURRENT.table)
-
- val migrationResolver = createMigrationResolver()
- val schemaVersionDAO = SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.table)
- val migrate = Migrate(
- migrationResolver,
- configs.target,
- schemaVersionDAO,
- session,
- keyspace.cluster.username,
- configs.isAllowOutOfOrder
- )
+ return execute(migrateAction())
+ }
- return migrate.run()
- }
- })
+ /**
+ * Starts the database migration. All pending migrations will be applied in order.
+ * Calling migrate on an up-to-date database has no effect.
+ *
+ * @param session The Cassandra connection session.
+ * @return The number of successfully applied migrations.
+ */
+ fun migrate(session: Session): Int {
+ return execute(migrateAction(), session)
}
/**
@@ -114,16 +107,18 @@ class CassandraMigration : CassandraMigrationConfiguration {
* @return All migrations sorted by version, oldest first.
*/
fun info(): MigrationInfoService {
- return execute(object : Action {
- override fun execute(session: Session): MigrationInfoService {
- val migrationResolver = createMigrationResolver()
- val schemaVersionDAO = SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.table)
- val migrationInfoService = MigrationInfoServiceImpl(migrationResolver, schemaVersionDAO, configs.target, false, true)
- migrationInfoService.refresh()
+ return execute(infoAction())
+ }
- return migrationInfoService
- }
- })
+ /**
+ * Retrieves the complete information about all the migrations including applied, pending and current migrations with
+ * details and status.
+ *
+ * @param session The Cassandra connection session.
+ * @return All migrations sorted by version, oldest first.
+ */
+ fun info(session: Session): MigrationInfoService {
+ return execute(infoAction(), session)
}
/**
@@ -136,17 +131,29 @@ class CassandraMigration : CassandraMigrationConfiguration {
* * versions have been resolved that haven't been applied yet
*/
fun validate() {
- val validationError = execute(object : Action {
- override fun execute(session: Session): String? {
- val migrationResolver = createMigrationResolver()
- val schemaVersionDAO = SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.table)
- val validate = Validate(migrationResolver, configs.target, schemaVersionDAO, true, false)
- return validate.run()
- }
- })
+ val validationError = execute(validateAction())
+
+ if (validationError != null) {
+ throw CassandraMigrationException("Validation failed. $validationError")
+ }
+ }
+
+ /**
+ * Validate applied migrations against resolved ones (on the filesystem or classpath)
+ * to detect accidental changes that may prevent the schema(s) from being recreated exactly.
+ *
+ * Validation fails if:
+ * * differences in migration names, types or checksums are found
+ * * versions have been applied that aren't resolved locally anymore
+ * * versions have been resolved that haven't been applied yet
+ *
+ * @param session The Cassandra connection session.
+ */
+ fun validate(session: Session) {
+ val validationError = execute(validateAction(), session)
if (validationError != null) {
- throw CassandraMigrationException("Validation failed. " + validationError)
+ throw CassandraMigrationException("Validation failed. $validationError")
}
}
@@ -154,14 +161,16 @@ class CassandraMigration : CassandraMigrationConfiguration {
* Baselines an existing database, excluding all migrations up to and including baselineVersion.
*/
fun baseline() {
- execute(object : Action {
- override fun execute(session: Session): Unit {
- val migrationResolver = createMigrationResolver()
- val schemaVersionDAO = SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.table)
- val baseline = Baseline(migrationResolver, baselineVersion, schemaVersionDAO, baselineDescription, keyspace.cluster.username)
- baseline.run()
- }
- })
+ execute(baselineAction())
+ }
+
+ /**
+ * Baselines an existing database, excluding all migrations up to and including baselineVersion.
+ *
+ * @param session The Cassandra connection session.
+ */
+ fun baseline(session: Session) {
+ execute(baselineAction(), session)
}
/**
@@ -179,16 +188,17 @@ class CassandraMigration : CassandraMigrationConfiguration {
var cluster: Cluster? = null
var session: Session? = null
try {
- if (null == keyspace)
- throw IllegalArgumentException("Unable to establish Cassandra session. Keyspace is not configured.")
-
- if (null == keyspace.cluster)
- throw IllegalArgumentException("Unable to establish Cassandra session. Cluster is not configured.")
+ // Guard clauses: Cluster and Keyspace must be defined
+ val errorMsg = "Unable to establish Cassandra session"
+ if (keyspace == null) throw IllegalArgumentException("$errorMsg. Keyspace is not configured.")
+ if (keyspace.cluster == null) throw IllegalArgumentException("$errorMsg. Cluster is not configured.")
+ if (keyspace.name.isNullOrEmpty()) throw IllegalArgumentException("$errorMsg. Keyspace is not specified.")
+ // Build the Cluster
val builder = Cluster.Builder()
builder.addContactPoints(*keyspace.cluster.contactpoints).withPort(keyspace.cluster.port)
- if (null != keyspace.cluster.username && !keyspace.cluster.username.trim { it <= ' ' }.isEmpty()) {
- if (null != keyspace.cluster.password && !keyspace.cluster.password.trim { it <= ' ' }.isEmpty()) {
+ if (!keyspace.cluster.username.isNullOrBlank()) {
+ if (!keyspace.cluster.password.isNullOrBlank()) {
builder.withCredentials(keyspace.cluster.username, keyspace.cluster.password)
} else {
throw IllegalArgumentException("Password must be provided with username.")
@@ -196,23 +206,19 @@ class CassandraMigration : CassandraMigrationConfiguration {
}
cluster = builder.build()
- val metadata = cluster!!.metadata
- LOG.info(getConnectionInfo(metadata))
+ LOG.info(getConnectionInfo(cluster.metadata))
+ // Create a new Session
session = cluster.newSession()
- if (null == keyspace.name || keyspace.name.trim { it <= ' ' }.length == 0)
- throw IllegalArgumentException("Keyspace not specified.")
-
- val keyspaces = metadata.keyspaces
- var keyspaceExists = false
- for (keyspaceMetadata in keyspaces) {
- if (keyspaceMetadata.name.equals(keyspace.name, ignoreCase = true))
- keyspaceExists = true
+
+ // Connect to the specific Keyspace context (if already defined)
+ val keyspaces = cluster.metadata.keyspaces.map { it.name }
+ val keyspaceExists = keyspaces.first { it.equals(keyspace.name, ignoreCase = true) }.isNotEmpty()
+ if (keyspaceExists) {
+ session = cluster.connect(keyspace.name)
+ } else {
+ throw CassandraMigrationException("Keyspace: ${keyspace.name} does not exist.")
}
- if (keyspaceExists)
- session!!.execute("USE " + keyspace.name)
- else
- throw CassandraMigrationException("Keyspace: " + keyspace.name + " does not exist.")
result = action.execute(session)
} finally {
@@ -229,11 +235,22 @@ class CassandraMigration : CassandraMigrationConfiguration {
} catch (e: Exception) {
LOG.warn("Error closing Cassandra cluster")
}
-
}
return result
}
+ /**
+ * Executes this command with an existing session, with proper resource handling and cleanup.
+ *
+ * @param action The action to execute.
+ * @param session The Cassandra connection session.
+ * @param T The action result type.
+ * @return The action result.
+ */
+ internal fun execute(action: Action, session: Session): T {
+ return action.execute(session)
+ }
+
/**
* Get Cassandra connection information.
*
@@ -263,6 +280,102 @@ class CassandraMigration : CassandraMigrationConfiguration {
return CompositeMigrationResolver(classLoader, ScriptsLocations(*configs.scriptsLocations), configs.encoding)
}
+ /**
+ * Creates the SchemaVersionDAO.
+ *
+ * @return A configured SchemaVersionDAO instance.
+ */
+ private fun createSchemaVersionDAO(session: Session): SchemaVersionDAO {
+ return SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.table)
+ }
+
+ /**
+ * @return The database migration action.
+ */
+ private fun migrateAction(): Action {
+ return object: Action {
+ override fun execute(session: Session): Int {
+ Initialize().run(session, keyspace, MigrationVersion.CURRENT.table)
+
+ val migrationResolver = createMigrationResolver()
+ val schemaVersionDAO = createSchemaVersionDAO(session)
+ val migrate = Migrate(
+ migrationResolver,
+ configs.target,
+ schemaVersionDAO,
+ session,
+ keyspace.cluster.username,
+ configs.isAllowOutOfOrder
+ )
+
+ return migrate.run()
+ }
+ }
+ }
+
+ /**
+ * @return The migration info service action.
+ */
+ private fun infoAction(): Action {
+ return object : Action {
+ override fun execute(session: Session): MigrationInfoService {
+ val migrationResolver = createMigrationResolver()
+ val schemaVersionDAO = createSchemaVersionDAO(session)
+ val migrationInfoService = MigrationInfoServiceImpl(
+ migrationResolver,
+ schemaVersionDAO,
+ configs.target,
+ outOfOrder = false,
+ pendingOrFuture = true
+ )
+ migrationInfoService.refresh()
+
+ return migrationInfoService
+ }
+ }
+ }
+
+ /**
+ * @return The migration validation action.
+ */
+ private fun validateAction(): Action {
+ return object : Action {
+ override fun execute(session: Session): String? {
+ val migrationResolver = createMigrationResolver()
+ val schemaVersionDAO = createSchemaVersionDAO(session)
+ val validate = Validate(
+ migrationResolver,
+ configs.target,
+ schemaVersionDAO,
+ outOfOrder = true,
+ pendingOrFuture = false
+ )
+
+ return validate.run()
+ }
+ }
+ }
+
+ /**
+ * @return The migration baselining action.
+ */
+ private fun baselineAction(): Action {
+ return object : Action {
+ override fun execute(session: Session): Unit {
+ val migrationResolver = createMigrationResolver()
+ val schemaVersionDAO = createSchemaVersionDAO(session)
+ val baseline = Baseline(
+ migrationResolver,
+ baselineVersion,
+ schemaVersionDAO,
+ baselineDescription,
+ keyspace.cluster.username
+ )
+ baseline.run()
+ }
+ }
+ }
+
/**
* A Cassandra migration action that can be executed.
*
diff --git a/src/main/java/com/builtamont/cassandra/migration/internal/dbsupport/SchemaVersionDAO.java b/src/main/java/com/builtamont/cassandra/migration/internal/dbsupport/SchemaVersionDAO.java
index be6ae8e..d12db20 100755
--- a/src/main/java/com/builtamont/cassandra/migration/internal/dbsupport/SchemaVersionDAO.java
+++ b/src/main/java/com/builtamont/cassandra/migration/internal/dbsupport/SchemaVersionDAO.java
@@ -382,7 +382,7 @@ private int calculateInstalledRank() {
Select select = QueryBuilder
.select("count")
- .from(tableName + COUNTS_TABLE_NAME_SUFFIX);
+ .from(keyspace.getName(), tableName + COUNTS_TABLE_NAME_SUFFIX);
select.where(eq("name", "installed_rank"));
select.setConsistencyLevel(this.consistencyLevel);
diff --git a/src/test/java/com/builtamont/cassandra/migration/BaseIT.java b/src/test/java/com/builtamont/cassandra/migration/BaseIT.java
index 61f233d..8397637 100644
--- a/src/test/java/com/builtamont/cassandra/migration/BaseIT.java
+++ b/src/test/java/com/builtamont/cassandra/migration/BaseIT.java
@@ -1,3 +1,21 @@
+/**
+ * File : BaseIT.java
+ * License :
+ * Original - Copyright (c) 2015 - 2016 Contrast Security
+ * Derivative - Copyright (c) 2016 Citadel Technology Solutions Pte Ltd
+ *
+ * Licensed 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 com.builtamont.cassandra.migration;
import com.builtamont.cassandra.migration.config.Keyspace;
@@ -12,7 +30,8 @@
import org.junit.BeforeClass;
public abstract class BaseIT {
- public static final String CASSANDRA__KEYSPACE = "cassandra_migration_test";
+
+ public static final String CASSANDRA_KEYSPACE = "cassandra_migration_test";
public static final String CASSANDRA_CONTACT_POINT = "localhost";
public static final int CASSANDRA_PORT = 9147;
public static final String CASSANDRA_USERNAME = "cassandra";
@@ -36,23 +55,26 @@ public static void afterSuite() {
@Before
public void createKeyspace() {
Statement statement = new SimpleStatement(
- "CREATE KEYSPACE " + CASSANDRA__KEYSPACE +
+ "CREATE KEYSPACE " + CASSANDRA_KEYSPACE +
" WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };"
);
getSession(getKeyspace()).execute(statement);
+
+ // Reconnect session to the keyspace
+ session = session.getCluster().connect(CASSANDRA_KEYSPACE);
}
@After
public void dropKeyspace() {
Statement statement = new SimpleStatement(
- "DROP KEYSPACE " + CASSANDRA__KEYSPACE + ";"
+ "DROP KEYSPACE " + CASSANDRA_KEYSPACE + ";"
);
getSession(getKeyspace()).execute(statement);
}
protected Keyspace getKeyspace() {
Keyspace ks = new Keyspace();
- ks.setName(CASSANDRA__KEYSPACE);
+ ks.setName(CASSANDRA_KEYSPACE);
ks.getCluster().setContactpoints(CASSANDRA_CONTACT_POINT);
ks.getCluster().setPort(CASSANDRA_PORT);
ks.getCluster().setUsername(CASSANDRA_USERNAME);
@@ -60,13 +82,16 @@ protected Keyspace getKeyspace() {
return ks;
}
- private Session getSession(Keyspace keyspace) {
+ protected Session getSession(Keyspace keyspace) {
if (session != null && !session.isClosed())
return session;
- com.datastax.driver.core.Cluster.Builder builder = new com.datastax.driver.core.Cluster.Builder();
- builder.addContactPoints(CASSANDRA_CONTACT_POINT).withPort(CASSANDRA_PORT);
- builder.withCredentials(keyspace.getCluster().getUsername(), keyspace.getCluster().getPassword());
+ String username = keyspace.getCluster().getUsername();
+ String password = keyspace.getCluster().getPassword();
+ Cluster.Builder builder = new Cluster.Builder();
+ builder.addContactPoints(CASSANDRA_CONTACT_POINT)
+ .withPort(CASSANDRA_PORT)
+ .withCredentials(username, password);
Cluster cluster = builder.build();
session = cluster.connect();
return session;
diff --git a/src/test/java/com/builtamont/cassandra/migration/CassandraMigrationIT.java b/src/test/java/com/builtamont/cassandra/migration/CassandraMigrationIT.java
index dfbc048..1cee1ba 100644
--- a/src/test/java/com/builtamont/cassandra/migration/CassandraMigrationIT.java
+++ b/src/test/java/com/builtamont/cassandra/migration/CassandraMigrationIT.java
@@ -1,5 +1,5 @@
/**
- * File : CassandraMigrationIT.kt
+ * File : CassandraMigrationIT.java
* License :
* Original - Copyright (c) 2015 - 2016 Contrast Security
* Derivative - Copyright (c) 2016 Citadel Technology Solutions Pte Ltd
@@ -24,6 +24,7 @@
import com.builtamont.cassandra.migration.internal.metadatatable.AppliedMigration;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import org.hamcrest.Matchers;
@@ -40,243 +41,305 @@
public class CassandraMigrationIT extends BaseIT {
- @Test
- public void runApiTest() {
- String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
- CassandraMigration cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(scriptsLocations);
- cm.setKeyspace(getKeyspace());
- cm.migrate();
-
- MigrationInfoService infoService = cm.info();
- System.out.println("Initial migration");
- System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
- assertThat(infoService.all().length, is(4));
- for (MigrationInfo info : infoService.all()) {
- assertThat(info.getVersion().getVersion(), anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1")));
- if (info.getVersion().equals("3.0.1")) {
- assertThat(info.getDescription(), is("Three point zero one"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.JAVA_DRIVER.name()));
- assertThat(info.getScript().contains(".java"), is(true));
-
- Select select = QueryBuilder.select().column("value").from("test1");
- select.where(eq("space", "web")).and(eq("key", "facebook"));
- ResultSet result = getSession().execute(select);
- assertThat(result.one().getString("value"), is("facebook.com"));
- } else if (info.getVersion().equals("3.0")) {
- assertThat(info.getDescription(), is("Third"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.JAVA_DRIVER.name()));
- assertThat(info.getScript().contains(".java"), is(true));
-
- Select select = QueryBuilder.select().column("value").from("test1");
- select.where(eq("space", "web")).and(eq("key", "google"));
- ResultSet result = getSession().execute(select);
- assertThat(result.one().getString("value"), is("google.com"));
- } else if (info.getVersion().equals("2.0.0")) {
- assertThat(info.getDescription(), is("Second"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
- assertThat(info.getScript().contains(".cql"), is(true));
-
- Select select = QueryBuilder.select().column("title").column("message").from("contents");
- select.where(eq("id", 1));
- Row row = getSession().execute(select).one();
- assertThat(row.getString("title"), is("foo"));
- assertThat(row.getString("message"), is("bar"));
- } else if (info.getVersion().equals("1.0.0")) {
- assertThat(info.getDescription(), is("First"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
- assertThat(info.getScript().contains(".cql"), is(true));
-
- Select select = QueryBuilder.select().column("value").from("test1");
- select.where(eq("space", "foo")).and(eq("key", "bar"));
- ResultSet result = getSession().execute(select);
- assertThat(result.one().getString("value"), is("profit!"));
- }
-
- assertThat(info.getState().isApplied(), is(true));
- assertThat(info.getInstalledOn(), notNullValue());
- }
-
- // test out of order when out of order is not allowed
- String[] outOfOrderScriptsLocations = { "migration/integ_outoforder", "migration/integ/java" };
- cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(outOfOrderScriptsLocations);
- cm.setKeyspace(getKeyspace());
- cm.migrate();
-
- infoService = cm.info();
- System.out.println("Out of order migration with out-of-order ignored");
- System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
- assertThat(infoService.all().length, is(5));
- for (MigrationInfo info : infoService.all()) {
- assertThat(info.getVersion().getVersion(),
- anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1"), is("1.1.1")));
- if (info.getVersion().equals("1.1.1")) {
- assertThat(info.getDescription(), is("Late arrival"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
- assertThat(info.getScript().contains(".cql"), is(true));
- assertThat(info.getState().isApplied(), is(false));
- assertThat(info.getInstalledOn(), nullValue());
- }
- }
-
- // test out of order when out of order is allowed
- String[] outOfOrder2ScriptsLocations = { "migration/integ_outoforder2", "migration/integ/java" };
- cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(outOfOrder2ScriptsLocations);
- cm.getConfigs().setAllowOutOfOrder(true);
- cm.setKeyspace(getKeyspace());
- cm.migrate();
-
- infoService = cm.info();
- System.out.println("Out of order migration with out-of-order allowed");
- System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
- assertThat(infoService.all().length, is(6));
- for (MigrationInfo info : infoService.all()) {
- assertThat(info.getVersion().getVersion(),
- anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1"), is("1.1.1"), is("1.1.2")));
- if (info.getVersion().equals("1.1.2")) {
- assertThat(info.getDescription(), is("Late arrival2"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
- assertThat(info.getScript().contains(".cql"), is(true));
- assertThat(info.getState().isApplied(), is(true));
- assertThat(info.getInstalledOn(), notNullValue());
- }
- }
-
- // test out of order when out of order is allowed again
- String[] outOfOrder3ScriptsLocations = { "migration/integ_outoforder3", "migration/integ/java" };
- cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(outOfOrder3ScriptsLocations);
- cm.getConfigs().setAllowOutOfOrder(true);
- cm.setKeyspace(getKeyspace());
- cm.migrate();
-
- infoService = cm.info();
- System.out.println("Out of order migration with out-of-order allowed");
- System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
- assertThat(infoService.all().length, is(7));
- for (MigrationInfo info : infoService.all()) {
- assertThat(info.getVersion().getVersion(),
- anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1"), is("1.1.1"), is("1.1.2"), is("1.1.3")));
- if (info.getVersion().equals("1.1.3")) {
- assertThat(info.getDescription(), is("Late arrival3"));
- assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
- assertThat(info.getScript().contains(".cql"), is(true));
- assertThat(info.getState().isApplied(), is(true));
- assertThat(info.getInstalledOn(), notNullValue());
- }
- }
- }
-
- @Test
- public void testValidate() {
- // apply migration scripts
- String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
- CassandraMigration cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(scriptsLocations);
- cm.setKeyspace(getKeyspace());
- cm.migrate();
-
- MigrationInfoService infoService = cm.info();
- String validationError = infoService.validate();
- Assert.assertNull(validationError);
-
- cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(scriptsLocations);
- cm.setKeyspace(getKeyspace());
-
- cm.validate();
-
- cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(new String[] { "migration/integ/java" });
- cm.setKeyspace(getKeyspace());
-
- try {
- cm.validate();
- Assert.fail("The expected CassandraMigrationException was not raised");
- } catch (CassandraMigrationException e) {
- Assert.assertTrue("expected CassandraMigrationException", true);
- }
- }
-
- static boolean runCmdTestCompleted = false;
- static boolean runCmdTestSuccess = false;
-
- @Test
- public void runCmdTest() throws IOException, InterruptedException {
- String shell = "java -jar"
- + " -Dcassandra.migration.scripts.locations=filesystem:target/test-classes/migration/integ"
- + " -Dcassandra.migration.cluster.contactpoints=" + CASSANDRA_CONTACT_POINT
- + " -Dcassandra.migration.cluster.port=" + CASSANDRA_PORT
- + " -Dcassandra.migration.cluster.username=" + CASSANDRA_USERNAME
- + " -Dcassandra.migration.cluster.password=" + CASSANDRA_PASSWORD
- + " -Dcassandra.migration.keyspace.name=" + CASSANDRA__KEYSPACE
- + " target/*-jar-with-dependencies.jar" + " migrate";
- ProcessBuilder builder;
- if (isWindows()) {
- throw new IllegalStateException();
- } else {
- builder = new ProcessBuilder("bash", "-c", shell);
- }
- builder.redirectErrorStream(true);
- final Process process = builder.start();
-
- watch(process);
-
- while (!runCmdTestCompleted)
- Thread.sleep(1000L);
-
- assertThat(runCmdTestSuccess, is(true));
- }
-
- @Test
- public void testBaseLine(){
- String[] scriptsLocations = {"migration/integ", "migration/integ/java"};
- CassandraMigration cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(scriptsLocations);
- cm.setKeyspace(getKeyspace());
- cm.baseline();
-
- SchemaVersionDAO schemaVersionDAO = new SchemaVersionDAO(getSession(), getKeyspace(), MigrationVersion.Companion.getCURRENT().getTable());
- AppliedMigration baselineMarker = schemaVersionDAO.getBaselineMarker();
- assertThat(baselineMarker.getVersion(), is(MigrationVersion.Companion.fromVersion("1")));
- }
-
- @Test(expected = CassandraMigrationException.class)
- public void testBaseLineWithMigrations() {
- String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
- CassandraMigration cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(scriptsLocations);
- cm.setKeyspace(getKeyspace());
- cm.migrate();
-
- cm = new CassandraMigration();
- cm.getConfigs().setScriptsLocations(scriptsLocations);
- cm.setKeyspace(getKeyspace());
- cm.baseline();
- }
-
- private static void watch(final Process process) {
- new Thread(new Runnable() {
- public void run() {
- BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line;
- try {
- while ((line = input.readLine()) != null) {
- if (line.contains("Successfully applied 2 migration(s)"))
- runCmdTestSuccess = true;
- System.out.println(line);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- runCmdTestCompleted = true;
- }
- }).start();
- }
-
- private boolean isWindows() {
- return (System.getProperty("os.name").toLowerCase()).contains("win");
- }
+ @Test
+ public void runApiTest() {
+ String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate();
+
+ MigrationInfoService infoService = cm.info();
+ System.out.println("Initial migration");
+ System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
+ assertThat(infoService.all().length, is(4));
+ for (MigrationInfo info : infoService.all()) {
+ assertThat(info.getVersion().getVersion(), anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1")));
+ if (info.getVersion().equals("3.0.1")) {
+ assertThat(info.getDescription(), is("Three point zero one"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.JAVA_DRIVER.name()));
+ assertThat(info.getScript().contains(".java"), is(true));
+
+ Select select = QueryBuilder.select().column("value").from("test1");
+ select.where(eq("space", "web")).and(eq("key", "facebook"));
+ ResultSet result = getSession().execute(select);
+ assertThat(result.one().getString("value"), is("facebook.com"));
+ } else if (info.getVersion().equals("3.0")) {
+ assertThat(info.getDescription(), is("Third"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.JAVA_DRIVER.name()));
+ assertThat(info.getScript().contains(".java"), is(true));
+
+ Select select = QueryBuilder.select().column("value").from("test1");
+ select.where(eq("space", "web")).and(eq("key", "google"));
+ ResultSet result = getSession().execute(select);
+ assertThat(result.one().getString("value"), is("google.com"));
+ } else if (info.getVersion().equals("2.0.0")) {
+ assertThat(info.getDescription(), is("Second"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
+ assertThat(info.getScript().contains(".cql"), is(true));
+
+ Select select = QueryBuilder.select().column("title").column("message").from("contents");
+ select.where(eq("id", 1));
+ Row row = getSession().execute(select).one();
+ assertThat(row.getString("title"), is("foo"));
+ assertThat(row.getString("message"), is("bar"));
+ } else if (info.getVersion().equals("1.0.0")) {
+ assertThat(info.getDescription(), is("First"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
+ assertThat(info.getScript().contains(".cql"), is(true));
+
+ Select select = QueryBuilder.select().column("value").from("test1");
+ select.where(eq("space", "foo")).and(eq("key", "bar"));
+ ResultSet result = getSession().execute(select);
+ assertThat(result.one().getString("value"), is("profit!"));
+ }
+
+ assertThat(info.getState().isApplied(), is(true));
+ assertThat(info.getInstalledOn(), notNullValue());
+ }
+
+ // test out of order when out of order is not allowed
+ String[] outOfOrderScriptsLocations = { "migration/integ_outoforder", "migration/integ/java" };
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(outOfOrderScriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate();
+
+ infoService = cm.info();
+ System.out.println("Out of order migration with out-of-order ignored");
+ System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
+ assertThat(infoService.all().length, is(5));
+ for (MigrationInfo info : infoService.all()) {
+ assertThat(info.getVersion().getVersion(),
+ anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1"), is("1.1.1")));
+ if (info.getVersion().equals("1.1.1")) {
+ assertThat(info.getDescription(), is("Late arrival"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
+ assertThat(info.getScript().contains(".cql"), is(true));
+ assertThat(info.getState().isApplied(), is(false));
+ assertThat(info.getInstalledOn(), nullValue());
+ }
+ }
+
+ // test out of order when out of order is allowed
+ String[] outOfOrder2ScriptsLocations = { "migration/integ_outoforder2", "migration/integ/java" };
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(outOfOrder2ScriptsLocations);
+ cm.getConfigs().setAllowOutOfOrder(true);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate();
+
+ infoService = cm.info();
+ System.out.println("Out of order migration with out-of-order allowed");
+ System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
+ assertThat(infoService.all().length, is(6));
+ for (MigrationInfo info : infoService.all()) {
+ assertThat(info.getVersion().getVersion(),
+ anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1"), is("1.1.1"), is("1.1.2")));
+ if (info.getVersion().equals("1.1.2")) {
+ assertThat(info.getDescription(), is("Late arrival2"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
+ assertThat(info.getScript().contains(".cql"), is(true));
+ assertThat(info.getState().isApplied(), is(true));
+ assertThat(info.getInstalledOn(), notNullValue());
+ }
+ }
+
+ // test out of order when out of order is allowed again
+ String[] outOfOrder3ScriptsLocations = { "migration/integ_outoforder3", "migration/integ/java" };
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(outOfOrder3ScriptsLocations);
+ cm.getConfigs().setAllowOutOfOrder(true);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate();
+
+ infoService = cm.info();
+ System.out.println("Out of order migration with out-of-order allowed");
+ System.out.println(MigrationInfoDumper.INSTANCE.dumpToAsciiTable(infoService.all()));
+ assertThat(infoService.all().length, is(7));
+ for (MigrationInfo info : infoService.all()) {
+ assertThat(info.getVersion().getVersion(),
+ anyOf(is("1.0.0"), is("2.0.0"), is("3.0"), is("3.0.1"), is("1.1.1"), is("1.1.2"), is("1.1.3")));
+ if (info.getVersion().equals("1.1.3")) {
+ assertThat(info.getDescription(), is("Late arrival3"));
+ assertThat(info.getType().name(), Matchers.is(MigrationType.CQL.name()));
+ assertThat(info.getScript().contains(".cql"), is(true));
+ assertThat(info.getState().isApplied(), is(true));
+ assertThat(info.getInstalledOn(), notNullValue());
+ }
+ }
+ }
+
+ @Test
+ public void testValidate() {
+ // apply migration scripts
+ String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate();
+
+ MigrationInfoService infoService = cm.info();
+ String validationError = infoService.validate();
+ Assert.assertNull(validationError);
+
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+
+ cm.validate();
+
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(new String[] { "migration/integ/java" });
+ cm.setKeyspace(getKeyspace());
+
+ try {
+ cm.validate();
+ Assert.fail("The expected CassandraMigrationException was not raised");
+ } catch (CassandraMigrationException e) {
+ Assert.assertTrue("expected CassandraMigrationException", true);
+ }
+ }
+
+ @Test
+ public void testValidateWithSession() {
+ // apply migration scripts
+ String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
+ Session session = getSession();
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate(session);
+
+ MigrationInfoService infoService = cm.info(session);
+ String validationError = infoService.validate();
+ Assert.assertNull(validationError);
+
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+
+ cm.validate(session);
+
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(new String[] { "migration/integ/java" });
+ cm.setKeyspace(getKeyspace());
+
+ try {
+ cm.validate(session);
+ Assert.fail("The expected CassandraMigrationException was not raised");
+ } catch (CassandraMigrationException e) {
+ }
+
+ Assert.assertFalse(session.isClosed());
+ }
+
+ @Test
+ public void testBaseLine() {
+ String[] scriptsLocations = {"migration/integ", "migration/integ/java"};
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.baseline();
+
+ SchemaVersionDAO schemaVersionDAO = new SchemaVersionDAO(getSession(), getKeyspace(), MigrationVersion.Companion.getCURRENT().getTable());
+ AppliedMigration baselineMarker = schemaVersionDAO.getBaselineMarker();
+ assertThat(baselineMarker.getVersion(), is(MigrationVersion.Companion.fromVersion("1")));
+ }
+
+ @Test
+ public void testBaseLineWithSession() {
+ String[] scriptsLocations = {"migration/integ", "migration/integ/java"};
+ Session session = getSession();
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.baseline(session);
+
+ SchemaVersionDAO schemaVersionDAO = new SchemaVersionDAO(getSession(), getKeyspace(), MigrationVersion.Companion.getCURRENT().getTable());
+ AppliedMigration baselineMarker = schemaVersionDAO.getBaselineMarker();
+ assertThat(baselineMarker.getVersion(), is(MigrationVersion.Companion.fromVersion("1")));
+ }
+
+ @Test(expected = CassandraMigrationException.class)
+ public void testBaseLineWithMigrations() {
+ String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate();
+
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.baseline();
+ }
+
+ @Test(expected = CassandraMigrationException.class)
+ public void testBaseLineWithMigrationsWithSession() {
+ String[] scriptsLocations = { "migration/integ", "migration/integ/java" };
+ Session session = getSession();
+ CassandraMigration cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.migrate(session);
+
+ cm = new CassandraMigration();
+ cm.getConfigs().setScriptsLocations(scriptsLocations);
+ cm.setKeyspace(getKeyspace());
+ cm.baseline(session);
+ }
+
+ static boolean runCmdTestCompleted = false;
+ static boolean runCmdTestSuccess = false;
+
+ @Test
+ public void runCmdTest() throws IOException, InterruptedException {
+ String shell = "java -jar"
+ + " -Dcassandra.migration.scripts.locations=filesystem:target/test-classes/migration/integ"
+ + " -Dcassandra.migration.cluster.contactpoints=" + CASSANDRA_CONTACT_POINT
+ + " -Dcassandra.migration.cluster.port=" + CASSANDRA_PORT
+ + " -Dcassandra.migration.cluster.username=" + CASSANDRA_USERNAME
+ + " -Dcassandra.migration.cluster.password=" + CASSANDRA_PASSWORD
+ + " -Dcassandra.migration.keyspace.name=" + CASSANDRA_KEYSPACE
+ + " target/*-jar-with-dependencies.jar" + " migrate";
+ ProcessBuilder builder;
+ if (isWindows()) {
+ throw new IllegalStateException();
+ } else {
+ builder = new ProcessBuilder("bash", "-c", shell);
+ }
+ builder.redirectErrorStream(true);
+ final Process process = builder.start();
+
+ watch(process);
+
+ while (!runCmdTestCompleted)
+ Thread.sleep(1000L);
+
+ assertThat(runCmdTestSuccess, is(true));
+ }
+
+ private static void watch(final Process process) {
+ new Thread(new Runnable() {
+ public void run() {
+ BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line;
+ try {
+ while ((line = input.readLine()) != null) {
+ if (line.contains("Successfully applied 2 migration(s)"))
+ runCmdTestSuccess = true;
+ System.out.println(line);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ runCmdTestCompleted = true;
+ }
+ }).start();
+ }
+
+ private boolean isWindows() {
+ return (System.getProperty("os.name").toLowerCase()).contains("win");
+ }
}