diff --git a/pom.xml b/pom.xml index f8a5e50..949f9da 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,12 @@ 1.7.13 true + + org.slf4j + slf4j-simple + 1.7.13 + test + commons-logging commons-logging diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/CassandraMigration.java b/src/main/java/com/contrastsecurity/cassandra/migration/CassandraMigration.java index 985c0c3..1932e40 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/CassandraMigration.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/CassandraMigration.java @@ -1,7 +1,9 @@ package com.contrastsecurity.cassandra.migration; +import com.contrastsecurity.cassandra.migration.action.Baseline; import com.contrastsecurity.cassandra.migration.action.Initialize; import com.contrastsecurity.cassandra.migration.action.Migrate; +import com.contrastsecurity.cassandra.migration.action.Validate; import com.contrastsecurity.cassandra.migration.config.Keyspace; import com.contrastsecurity.cassandra.migration.config.MigrationConfigs; import com.contrastsecurity.cassandra.migration.config.ScriptsLocations; @@ -17,7 +19,6 @@ import com.datastax.driver.core.KeyspaceMetadata; import com.datastax.driver.core.Metadata; import com.datastax.driver.core.Session; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.util.List; @@ -28,6 +29,8 @@ public class CassandraMigration { private ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private Keyspace keyspace; private MigrationConfigs configs; + private MigrationVersion baselineVersion = MigrationVersion.fromVersion("1"); + private String baselineDescription = "<< Cassandra Baseline >>"; public CassandraMigration() { this.keyspace = new Keyspace(); @@ -59,7 +62,7 @@ public MigrationConfigs getConfigs() { return configs; } - private MigrationResolver createMigrationResolver() { + protected MigrationResolver createMigrationResolver() { return new CompositeMigrationResolver(classLoader, new ScriptsLocations(configs.getScriptsLocations()), configs.getEncoding()); } @@ -92,9 +95,33 @@ public MigrationInfoService execute(Session session) { }); } + public void validate() { + String validationError = execute(new Action() { + @Override + public String execute(Session session) { + MigrationResolver migrationResolver = createMigrationResolver(); + SchemaVersionDAO schemaVersionDao = new SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.getTable()); + Validate validate = new Validate(migrationResolver, schemaVersionDao, configs.getTarget(), true, false); + return validate.run(); + } + }); + + if (validationError != null) { + throw new CassandraMigrationException("Validation failed. " + validationError); + } + } + public void baseline() { - //TODO - throw new NotImplementedException(); + execute(new Action() { + @Override + public Void execute(Session session) { + MigrationResolver migrationResolver = createMigrationResolver(); + SchemaVersionDAO schemaVersionDao = new SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.getTable()); + Baseline baseLine = new Baseline(schemaVersionDao, migrationResolver, baselineVersion, baselineDescription ); + baseLine.run(); + return null; + } + }); } private String getConnectionInfo(Metadata metadata) { diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/CommandLine.java b/src/main/java/com/contrastsecurity/cassandra/migration/CommandLine.java index 69db1f5..c4fb4dc 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/CommandLine.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/CommandLine.java @@ -10,6 +10,21 @@ import java.util.List; public class CommandLine { + /** + * command to trigger migrate action + */ + public static final String MIGRATE = "migrate"; + + /** + * command to trigger validate action + */ + public static final String VALIDATE = "validate"; + + /** + * command to trigger baseline action + */ + public static final String BASELINE = "baseline"; + private static Log LOG; public static void main(String[] args) { @@ -22,10 +37,18 @@ public static void main(String[] args) { return; } + String operation = operations.get(0); + CassandraMigration cm = new CassandraMigration(); Keyspace ks = new Keyspace(); cm.setKeyspace(ks); - cm.migrate(); + if (MIGRATE.equalsIgnoreCase(operation)) { + cm.migrate(); + } else if (VALIDATE.equalsIgnoreCase(operation)) { + cm.validate(); + }else if(BASELINE.equalsIgnoreCase(operation)){ + cm.baseline(); + } } private static List determineOperations(String[] args) { @@ -67,6 +90,8 @@ private static void printUsage() { LOG.info("Commands"); LOG.info("========"); LOG.info("migrate : Migrates the database"); + LOG.info("validate : Validates the applied migrations against the available ones"); + LOG.info("baseline : Baselines an existing database, excluding all migrations upto and including baselineVersion"); LOG.info(""); LOG.info("Add -X to print debug output"); LOG.info("Add -q to suppress all output, except for errors and warnings"); diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/action/Baseline.java b/src/main/java/com/contrastsecurity/cassandra/migration/action/Baseline.java new file mode 100644 index 0000000..88b711a --- /dev/null +++ b/src/main/java/com/contrastsecurity/cassandra/migration/action/Baseline.java @@ -0,0 +1,42 @@ +package com.contrastsecurity.cassandra.migration.action; + +import com.contrastsecurity.cassandra.migration.CassandraMigrationException; +import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO; +import com.contrastsecurity.cassandra.migration.info.AppliedMigration; +import com.contrastsecurity.cassandra.migration.info.MigrationVersion; +import com.contrastsecurity.cassandra.migration.resolver.MigrationResolver; + +public class Baseline { + + private SchemaVersionDAO schemaVersionDao; + private MigrationResolver migrationResolver; + private MigrationVersion baselineVersion; + private String baselineDescription; + + public Baseline(SchemaVersionDAO schemaVersionDao, MigrationResolver migrationResolver, MigrationVersion baselineVersion, String baselineDescription) { + this.schemaVersionDao = schemaVersionDao; + this.migrationResolver = migrationResolver; + this.baselineVersion = baselineVersion; + this.baselineDescription = baselineDescription; + } + + public void run() { + AppliedMigration baselineMigration = schemaVersionDao.getBaselineMarker(); + if (schemaVersionDao.hasAppliedMigrations()) { + throw new CassandraMigrationException("Unable to baseline metadata table " + schemaVersionDao.getTableName() + " as it already contains migrations"); + } + if (schemaVersionDao.hasBaselineMarker()) { + if (!baselineMigration.getVersion().equals(baselineVersion) || !baselineMigration.getDescription().equals(baselineDescription)) { + throw new CassandraMigrationException("Unable to baseline metadata table " + schemaVersionDao.getTableName() + " with (" + baselineVersion + + "," + baselineDescription + ") as it has already been initialized with (" + baselineMigration.getVersion() + "," + baselineMigration + .getDescription() + ")"); + } + } else { + if (baselineVersion.equals(MigrationVersion.fromVersion("0"))) { + throw new CassandraMigrationException("Unable to baseline metadata table " + schemaVersionDao.getTableName() + " with version 0 as this " + + "version was used for schema creation"); + } + schemaVersionDao.addBaselineMarker(baselineVersion, baselineDescription); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/action/Migrate.java b/src/main/java/com/contrastsecurity/cassandra/migration/action/Migrate.java index ab41a13..6416b27 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/action/Migrate.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/action/Migrate.java @@ -2,7 +2,11 @@ import com.contrastsecurity.cassandra.migration.CassandraMigrationException; import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO; -import com.contrastsecurity.cassandra.migration.info.*; +import com.contrastsecurity.cassandra.migration.info.AppliedMigration; +import com.contrastsecurity.cassandra.migration.info.MigrationInfo; +import com.contrastsecurity.cassandra.migration.info.MigrationInfoService; +import com.contrastsecurity.cassandra.migration.info.MigrationState; +import com.contrastsecurity.cassandra.migration.info.MigrationVersion; import com.contrastsecurity.cassandra.migration.logging.Log; import com.contrastsecurity.cassandra.migration.logging.LogFactory; import com.contrastsecurity.cassandra.migration.resolver.MigrationExecutor; diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/action/Validate.java b/src/main/java/com/contrastsecurity/cassandra/migration/action/Validate.java new file mode 100644 index 0000000..c0988d4 --- /dev/null +++ b/src/main/java/com/contrastsecurity/cassandra/migration/action/Validate.java @@ -0,0 +1,61 @@ +package com.contrastsecurity.cassandra.migration.action; + +import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO; +import com.contrastsecurity.cassandra.migration.info.MigrationInfoService; +import com.contrastsecurity.cassandra.migration.info.MigrationVersion; +import com.contrastsecurity.cassandra.migration.logging.Log; +import com.contrastsecurity.cassandra.migration.logging.LogFactory; +import com.contrastsecurity.cassandra.migration.resolver.MigrationResolver; +import com.contrastsecurity.cassandra.migration.utils.StopWatch; +import com.contrastsecurity.cassandra.migration.utils.TimeFormat; + +/** + * Validates the applied migrations against the available ones. + */ +public class Validate { + + /** + * logging support + */ + private static final Log LOG = LogFactory.getLog(Validate.class); + + private SchemaVersionDAO schemaVersionDao; + + /** + * migration resolver + */ + private MigrationResolver migrationResolver; + + /** + * migration target + */ + private MigrationVersion migrationTarget; + + private boolean outOfOrder; + + private boolean pendingOrFuture; + + public Validate(MigrationResolver migrationResolver, SchemaVersionDAO schemaVersionDao, MigrationVersion migrationTarget, boolean outOfOrder, boolean pendingOrFuture) { + this.schemaVersionDao = schemaVersionDao; + this.migrationResolver = migrationResolver; + this.migrationTarget = migrationTarget; + this.outOfOrder = outOfOrder; + this.pendingOrFuture = pendingOrFuture; + } + + public String run() { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + MigrationInfoService infoService = new MigrationInfoService(migrationResolver, schemaVersionDao, migrationTarget, outOfOrder, pendingOrFuture); + infoService.refresh(); + int count = infoService.all().length; + String validationError = infoService.validate(); + + stopWatch.stop(); + + LOG.info(String.format("Validated %d migrations (execution time %s)", count, TimeFormat.format(stopWatch.getTotalTimeMillis()))); + + return validationError; + } +} \ No newline at end of file diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/dao/SchemaVersionDAO.java b/src/main/java/com/contrastsecurity/cassandra/migration/dao/SchemaVersionDAO.java index e3d647a..381f5d9 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/dao/SchemaVersionDAO.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/dao/SchemaVersionDAO.java @@ -7,15 +7,22 @@ import com.contrastsecurity.cassandra.migration.logging.Log; import com.contrastsecurity.cassandra.migration.logging.LogFactory; import com.contrastsecurity.cassandra.migration.utils.CachePrepareStatement; -import com.datastax.driver.core.*; +import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.SimpleStatement; +import com.datastax.driver.core.Statement; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Map; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.in; @@ -41,6 +48,10 @@ public Keyspace getKeyspace() { return this.keyspace; } + public String getTableName() { + return tableName; + } + public void createTablesIfNotExist() { if (tablesExist()) { return; @@ -104,27 +115,16 @@ public void addAppliedMigration(AppliedMigration appliedMigration) { MigrationVersion version = appliedMigration.getVersion(); int versionRank = calculateVersionRank(version); - PreparedStatement statement = cachePs.prepare( - "INSERT INTO " + keyspace.getName() + "." + tableName + + PreparedStatement statement = cachePs.prepare("INSERT INTO " + keyspace.getName() + "." + tableName + " (version_rank, installed_rank, version, description, type, script, checksum, installed_on," + " installed_by, execution_time, success)" + " VALUES" + - " (?, ?, ?, ?, ?, ?, ?, dateOf(now()), ?, ?, ?);" - ); + " (?, ?, ?, ?, ?, ?, ?, dateOf(now()), ?, ?, ?);"); statement.setConsistencyLevel(ConsistencyLevel.ALL); - session.execute(statement.bind( - versionRank, - calculateInstalledRank(), - version.toString(), - appliedMigration.getDescription(), - appliedMigration.getType().name(), - appliedMigration.getScript(), - appliedMigration.getChecksum(), - appliedMigration.getInstalledBy(), - appliedMigration.getExecutionTime(), - appliedMigration.isSuccess() - )); + session.execute(statement.bind(versionRank, calculateInstalledRank(), version.toString(), appliedMigration.getDescription(), appliedMigration.getType + ().name(), appliedMigration.getScript(), appliedMigration.getChecksum(), appliedMigration.getInstalledBy(), appliedMigration.getExecutionTime + (), appliedMigration.isSuccess())); LOG.debug("Schema version table " + tableName + " successfully updated to reflect changes"); } @@ -177,6 +177,58 @@ public List findAppliedMigrations() { return resultsList; } + /** + * Retrieve the applied migrations from the metadata table. + * + * @return The applied migrations. + */ + public List findAppliedMigrations(MigrationType... migrationTypes) { + if (!tablesExist()) { + return new ArrayList<>(); + } + + Select select = QueryBuilder + .select() + .column("version_rank") + .column("installed_rank") + .column("version") + .column("description") + .column("type") + .column("script") + .column("checksum") + .column("installed_on") + .column("installed_by") + .column("execution_time") + .column("success") + .from(keyspace.getName(), tableName); + + select.setConsistencyLevel(ConsistencyLevel.ALL); + ResultSet results = session.execute(select); + List resultsList = new ArrayList<>(); + List migTypeList = Arrays.asList(migrationTypes); + for (Row row : results) { + MigrationType migType = MigrationType.valueOf(row.getString("type")); + if(migTypeList.contains(migType)){ + resultsList.add(new AppliedMigration( + row.getInt("version_rank"), + row.getInt("installed_rank"), + MigrationVersion.fromVersion(row.getString("version")), + row.getString("description"), + migType, + row.getString("script"), + row.getInt("checksum"), + row.getDate("installed_on"), + row.getString("installed_by"), + row.getInt("execution_time"), + row.getBool("success") + )); + } + } + + //order by version_rank not necessary here as it eventually gets saved in TreeMap that uses natural ordering + + return resultsList; + } /** * Calculates the installed rank for the new migration to be inserted. * @@ -197,6 +249,21 @@ private int calculateInstalledRank() { return (int) result.one().getLong("count"); } + public boolean hasAppliedMigrations() { + if (!tablesExist()) { + return false; + } + createTablesIfNotExist(); + List filteredMigrations = new ArrayList<>(); + List appliedMigrations = findAppliedMigrations(); + for (AppliedMigration appliedMigration : appliedMigrations) { + if (!appliedMigration.getType().equals(MigrationType.BASELINE)) { + filteredMigrations.add(appliedMigration); + } + } + return !filteredMigrations.isEmpty(); + } + class MigrationMetaHolder { private int versionRank; @@ -257,4 +324,22 @@ private int calculateVersionRank(MigrationVersion version) { return migrationVersions.size() + 1; } + + public AppliedMigration getBaselineMarker() { + List appliedMigrations = findAppliedMigrations(MigrationType.BASELINE); + return appliedMigrations.isEmpty() ? null : appliedMigrations.get(0); + } + + public boolean hasBaselineMarker() { + if (!tablesExist()) { + return false; + } + createTablesIfNotExist(); + return !findAppliedMigrations(MigrationType.BASELINE).isEmpty(); + } + + public void addBaselineMarker(final MigrationVersion baselineVersion, final String baselineDescription) { + addAppliedMigration(new AppliedMigration(baselineVersion, baselineDescription, MigrationType.BASELINE, baselineDescription,0, null, + 0, true)); + } } diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/info/MigrationInfoService.java b/src/main/java/com/contrastsecurity/cassandra/migration/info/MigrationInfoService.java index f7e9a5c..a8a5e23 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/info/MigrationInfoService.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/info/MigrationInfoService.java @@ -19,7 +19,14 @@ import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO; import com.contrastsecurity.cassandra.migration.resolver.MigrationResolver; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; public class MigrationInfoService { diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolver.java b/src/main/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolver.java index c85c430..82e1fde 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolver.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolver.java @@ -98,6 +98,8 @@ ResolvedMigration extractMigrationInfo(JavaMigration javaMigration) { if (javaMigration instanceof MigrationChecksumProvider) { MigrationChecksumProvider checksumProvider = (MigrationChecksumProvider) javaMigration; checksum = checksumProvider.getChecksum(); + }else { + checksum = 0; } MigrationVersion version; diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/utils/FileCopyUtils.java b/src/main/java/com/contrastsecurity/cassandra/migration/utils/FileCopyUtils.java index 14c2b4a..889740c 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/utils/FileCopyUtils.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/utils/FileCopyUtils.java @@ -15,7 +15,13 @@ */ package com.contrastsecurity.cassandra.migration.utils; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; /** * Utility class for copying files and their contents. Inspired by Spring's own. diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathResource.java b/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathResource.java index f86717b..5139aa8 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathResource.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathResource.java @@ -19,7 +19,12 @@ import com.contrastsecurity.cassandra.migration.utils.FileCopyUtils; import com.contrastsecurity.cassandra.migration.utils.scanner.Resource; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.Charset; diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathScanner.java b/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathScanner.java index 7528388..33f0d71 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathScanner.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/classpath/ClassPathScanner.java @@ -27,7 +27,11 @@ import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLDecoder; -import java.util.*; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; public class ClassPathScanner { private static final Log LOG = LogFactory.getLog(ClassPathScanner.class); diff --git a/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/filesystem/FileSystemResource.java b/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/filesystem/FileSystemResource.java index 3480b61..1b2d3a8 100644 --- a/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/filesystem/FileSystemResource.java +++ b/src/main/java/com/contrastsecurity/cassandra/migration/utils/scanner/filesystem/FileSystemResource.java @@ -20,7 +20,12 @@ import com.contrastsecurity.cassandra.migration.utils.StringUtils; import com.contrastsecurity.cassandra.migration.utils.scanner.Resource; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.nio.charset.Charset; /** diff --git a/src/test/java/com/contrastsecurity/cassandra/migration/CassandraMigrationIT.java b/src/test/java/com/contrastsecurity/cassandra/migration/CassandraMigrationIT.java index c95588a..795499e 100644 --- a/src/test/java/com/contrastsecurity/cassandra/migration/CassandraMigrationIT.java +++ b/src/test/java/com/contrastsecurity/cassandra/migration/CassandraMigrationIT.java @@ -1,15 +1,18 @@ package com.contrastsecurity.cassandra.migration; import com.contrastsecurity.cassandra.migration.config.MigrationType; +import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO; +import com.contrastsecurity.cassandra.migration.info.AppliedMigration; import com.contrastsecurity.cassandra.migration.info.MigrationInfo; import com.contrastsecurity.cassandra.migration.info.MigrationInfoDumper; import com.contrastsecurity.cassandra.migration.info.MigrationInfoService; +import com.contrastsecurity.cassandra.migration.info.MigrationVersion; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; +import org.junit.Assert; import org.junit.Test; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.BufferedReader; import java.io.IOException; @@ -17,13 +20,15 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; public class CassandraMigrationIT extends BaseIT { - @Test public void runApiTest() { - String[] scriptsLocations = {"migration/integ", "migration/integ/java"}; + String[] scriptsLocations = { "migration/integ", "migration/integ/java" }; CassandraMigration cm = new CassandraMigration(); cm.getConfigs().setScriptsLocations(scriptsLocations); cm.setKeyspace(getKeyspace()); @@ -40,9 +45,7 @@ public void runApiTest() { assertThat(info.getType().name(), is(MigrationType.JAVA_DRIVER.name())); assertThat(info.getScript().contains(".java"), is(true)); - Select select = QueryBuilder.select() - .column("value") - .from("test1"); + 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")); @@ -51,9 +54,7 @@ public void runApiTest() { assertThat(info.getType().name(), is(MigrationType.JAVA_DRIVER.name())); assertThat(info.getScript().contains(".java"), is(true)); - Select select = QueryBuilder.select() - .column("value") - .from("test1"); + 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")); @@ -62,10 +63,7 @@ public void runApiTest() { assertThat(info.getType().name(), is(MigrationType.CQL.name())); assertThat(info.getScript().contains(".cql"), is(true)); - Select select = QueryBuilder.select() - .column("title") - .column("message") - .from("contents"); + 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")); @@ -75,9 +73,7 @@ public void runApiTest() { assertThat(info.getType().name(), is(MigrationType.CQL.name())); assertThat(info.getScript().contains(".cql"), is(true)); - Select select = QueryBuilder.select() - .column("value") - .from("test1"); + 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!")); @@ -88,7 +84,7 @@ public void runApiTest() { } // test out of order when out of order is not allowed - String[] outOfOrderScriptsLocations = {"migration/integ_outoforder", "migration/integ/java"}; + String[] outOfOrderScriptsLocations = { "migration/integ_outoforder", "migration/integ/java" }; cm = new CassandraMigration(); cm.getConfigs().setScriptsLocations(outOfOrderScriptsLocations); cm.setKeyspace(getKeyspace()); @@ -111,7 +107,7 @@ public void runApiTest() { } // test out of order when out of order is allowed - String[] outOfOrder2ScriptsLocations = {"migration/integ_outoforder2", "migration/integ/java"}; + String[] outOfOrder2ScriptsLocations = { "migration/integ_outoforder2", "migration/integ/java" }; cm = new CassandraMigration(); cm.getConfigs().setScriptsLocations(outOfOrder2ScriptsLocations); cm.getConfigs().setAllowOutOfOrder(true); @@ -135,7 +131,7 @@ public void runApiTest() { } // test out of order when out of order is allowed again - String[] outOfOrder3ScriptsLocations = {"migration/integ_outoforder3", "migration/integ/java"}; + String[] outOfOrder3ScriptsLocations = { "migration/integ_outoforder3", "migration/integ/java" }; cm = new CassandraMigration(); cm.getConfigs().setScriptsLocations(outOfOrder3ScriptsLocations); cm.getConfigs().setAllowOutOfOrder(true); @@ -159,24 +155,53 @@ public void runApiTest() { } } + @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("expected CassandraMigrationException but was no exception"); + } 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=" + BaseIT.CASSANDRA_CONTACT_POINT + - " -Dcassandra.migration.cluster.port=" + BaseIT.CASSANDRA_PORT + - " -Dcassandra.migration.cluster.username=" + BaseIT.CASSANDRA_USERNAME + - " -Dcassandra.migration.cluster.password=" + BaseIT.CASSANDRA_PASSWORD + - " -Dcassandra.migration.keyspace.name=" + BaseIT.CASSANDRA__KEYSPACE + - " target/*-jar-with-dependencies.jar" + - " migrate"; + String shell = "java -jar" + + " -Dcassandra.migration.scripts.locations=filesystem:target/test-classes/migration/integ" + + " -Dcassandra.migration.cluster.contactpoints=" + BaseIT.CASSANDRA_CONTACT_POINT + + " -Dcassandra.migration.cluster.port=" + BaseIT.CASSANDRA_PORT + + " -Dcassandra.migration.cluster.username=" + BaseIT.CASSANDRA_USERNAME + + " -Dcassandra.migration.cluster.password=" + BaseIT.CASSANDRA_PASSWORD + + " -Dcassandra.migration.keyspace.name=" + BaseIT.CASSANDRA__KEYSPACE + + " target/*-jar-with-dependencies.jar" + " migrate"; ProcessBuilder builder; if (isWindows()) { - throw new NotImplementedException(); + throw new IllegalStateException(); } else { builder = new ProcessBuilder("bash", "-c", shell); } @@ -191,6 +216,34 @@ public void runCmdTest() throws IOException, InterruptedException { assertThat(runCmdTestSuccess, is(true)); } + @Test + public void testBaseLine(){ + String[] scriptsLocations = {"migration/integ", "migration/integ/java"}; + CassandraMigration cm = new CassandraMigration(); + cm = new CassandraMigration(); + cm.getConfigs().setScriptsLocations(scriptsLocations); + cm.setKeyspace(getKeyspace()); + cm.baseline(); + + SchemaVersionDAO schemaVersionDao = new SchemaVersionDAO(getSession(), getKeyspace(), MigrationVersion.CURRENT.getTable()); + AppliedMigration baselineMarker = schemaVersionDao.getBaselineMarker(); + assertThat(baselineMarker.getVersion(), is(MigrationVersion.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() { @@ -200,7 +253,7 @@ public void run() { while ((line = input.readLine()) != null) { if (line.contains("Successfully applied 2 migrations")) runCmdTestSuccess = true; - //System.out.println(line); + System.out.println(line); } } catch (IOException e) { e.printStackTrace(); @@ -213,4 +266,4 @@ public void run() { private boolean isWindows() { return (System.getProperty("os.name").toLowerCase()).contains("win"); } -} +} \ No newline at end of file diff --git a/src/test/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolverTest.java b/src/test/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolverTest.java index 96b92f7..d659807 100644 --- a/src/test/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolverTest.java +++ b/src/test/java/com/contrastsecurity/cassandra/migration/resolver/java/JavaMigrationResolverTest.java @@ -27,7 +27,6 @@ import java.util.List; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; /** * Test for JavaMigrationResolver. @@ -51,7 +50,7 @@ public void resolveMigrations() { ResolvedMigration migrationInfo = migrationList.get(0); assertEquals("2", migrationInfo.getVersion().toString()); assertEquals("InterfaceBasedMigration", migrationInfo.getDescription()); - assertNull(migrationInfo.getChecksum()); + assertEquals(new Integer(0), migrationInfo.getChecksum()); ResolvedMigration migrationInfo1 = migrationList.get(1); assertEquals("3.5", migrationInfo1.getVersion().toString()); @@ -68,7 +67,7 @@ public void conventionOverConfiguration() { ResolvedMigration migrationInfo = jdbcMigrationResolver.extractMigrationInfo(new V2__InterfaceBasedMigration()); assertEquals("2", migrationInfo.getVersion().toString()); assertEquals("InterfaceBasedMigration", migrationInfo.getDescription()); - assertNull(migrationInfo.getChecksum()); + assertEquals(new Integer(0), migrationInfo.getChecksum()); } @Test