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