Skip to content
This repository was archived by the owner on Jul 22, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
language: java
jdk:
- openjdk7
- oraclejdk7
- oraclejdk8
env:
global:
- secure: "q+fkZBCN8ET5RrgaG4RGt1t1aSjsL6LN6BSt/Yvx2H5a2DtGmNA/A/gcAEnKlyv0BgXAcrzAzCCIgXvt2P4om5DcBU/yOTEga+/46r7+iVnmfQGcW81NHQA1rlIYvuBqXGDo9yo1B3eRr8vTj3fzEE3K8jjchHQUlgRdUum3DNKeZwACodV2fpj9ZslyoX4HRpWg3ctqvB0R7/4NwtXnXrOw8hHDF8OrQK3JiCxAoZnA16/Fwc0d8yN4Or10N1XiWbNdLFek+Y3nVTdGRUZjsqp/VhvgIwzmtnuiCeF2iuMCpYy6C9SAFG4Tyn5VLmFzEqXMrbxuMBp8c2GCcFQb0jxEiWKsT0Nufqc1pYlUSl2S12D8yokEo5H9/NcH/2p2b5zqzcWzFe1c5YEn0Ktj8d/01GDYfkuPyoQ0UmjC6h68iozk5mogPT0t7eUf7i0wll72v4kGB2xOK3VY+53LA7DS+f/0HeDi47tXgPkA8bg2dGZTTD+JHXcqyMTt9Ey96a42cauLQ4PGfujc6fPJUw31sxx2IURj1USdxut/a5PEa+LL+xGrKKgOW4GMUwjrYMnLf9e3Y/uR4EoHmYYwsoNtD0g6bEt7C83JZrQd5Sp3mN6gEU0sjp2/iBPqS+tB3z6eRUur8ctnk6EC82WHmRwZHeoKLVOktAPCKumBnWY="
- secure: "04af3/9b67O5xd1U8GDhCqRQedHM3RP5HokdsOwAe8vN3EyyyKWXQafBkKsPvmDh5Uu/CYQppOYS4pQxB9ikweTfj34DbyyxpqJmjYE4KFvdQuFd0msyXhLCg6xfvS4KO6zjUQ8/c3rNQap4hx/icQ50/NES4rkUkxIZ/VKQ4jPXcBzPegEC6Le50Vw2tR8FT4erdNuABnGf1WnWGUUa3i6xdQQPyw8kdTIun08HxE6M9F+JJRH8jH3b7KizQhGdACAk4fnCOmFSgu7pm6ACXRJYqAfg055i5mr77yZXfeUIcIY3l45uY1uR8sxEbLUE/KwwlLGLVZWDI4xU6JIGisbrmMce+vz6YKUT9gHF3iAEJ5e4N18nJcRyHVrqcuRzv5Py0rFPZ70dr7aW/tk0JrTz6+FZ4FNIOdvIQe4qWy2TVns0EkERdtYGTdsigWfa/sKF/P5+/2foUOlnR06p55NHpIjaHRKy/XFVV1gyURUlRUGExVoIMX21bAMxGYMFMH7LfddRsly028lXwibRMkQGBeyVRYKQmqJvN3mTPbuAWmZZdaVpqn1jkgETlT6/qz43zv9y8jAOzZ22SeHEXe3NiexChqkAJWIH3cBYshMhy8H1fmYAIVYHvI+BPsbi+qDYSnAlAUDqoLWXPAvWUX89dAIXYRNcKplzpFsjWvM="
matrix:
include:
- jdk: openjdk7
script: mvn test
- jdk: oraclejdk7
script: mvn test
- jdk: oraclejdk8
script:
- mvn test
- mvn verify
deploy:
provider: script
script: ./scripts/deploy.sh
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Import this library as a dependency (Maven example):
</dependency>
```

***NOTE:** Integration test in Travis CI is only run against Oracle JDK 1.8, due to embedded Cassandra's dependencies on JDK 1.8*

### Migration version table

``` shell
Expand Down Expand Up @@ -140,7 +142,6 @@ Keyspace:

### Limitations

* Baselining not supported yet
* The tool does not roll back the database upon migration failure. You're expected to manually restore backup.

## Project Rationale
Expand All @@ -161,6 +162,14 @@ There are various reasons why Kotlin was chosen, but three main reasons are:
* stronger `null` checks (enforced at the compiler level), and
* better Java collection support (e.g. additional functional features)

## Testing

Run `mvn test` to run the unit tests.

Run `mvn verify` to run the integration tests.

***NOTE:** The integration test might complain about some missing SIGAR binaries, this can be safely ignored. If you wish, you can download the missing binaries and set `java.library.path` parameter to point to the containing folder (e.g. `mvn verify -Djava.library.path=lib` where `lib` is the `/lib` folder relative to the project root).*

## Contributing

We follow the "[fork-and-pull]" Git workflow.
Expand Down Expand Up @@ -207,4 +216,5 @@ https://github.com/builtamont/cassandra-migration/releases
[Flyway]: https://flywaydb.org/
[Flyway's project license page]: https://github.com/flyway/flyway/blob/master/LICENSE
[fork-and-pull]: https://help.github.com/articles/using-pull-requests
[LICENSE]: LICENSE
[LICENSE]: LICENSE
[SIGAR]: https://support.hyperic.com/display/SIGAR/Home
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,13 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.19</version>
<version>2.19.1</version>
<!-- NOTE: Configuration as per http://stackoverflow.com/a/33757854 -->
<configuration>
<forkCount>3</forkCount>
<reuseForks>true</reuseForks>
<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>
</configuration>
<executions>
<execution>
<goals>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.builtamont.cassandra.migration.api.configuration.CassandraMigrationCo
import com.builtamont.cassandra.migration.api.configuration.MigrationConfigs
import com.builtamont.cassandra.migration.api.resolver.MigrationResolver
import com.builtamont.cassandra.migration.config.Keyspace
import com.builtamont.cassandra.migration.internal.command.Baseline
import com.builtamont.cassandra.migration.internal.command.Initialize
import com.builtamont.cassandra.migration.internal.command.Migrate
import com.builtamont.cassandra.migration.internal.command.Validate
Expand Down Expand Up @@ -61,6 +62,16 @@ class CassandraMigration : CassandraMigrationConfiguration {
*/
lateinit var configs: MigrationConfigs

/**
* The baseline version.
*/
private val baselineVersion = MigrationVersion.Companion.fromVersion("1")

/**
* The baseline description.
*/
private val baselineDescription = "<< Cassandra Baseline >>"

/**
* CassandraMigration initialization.
*/
Expand Down Expand Up @@ -104,7 +115,7 @@ class CassandraMigration : CassandraMigrationConfiguration {
*/
fun info(): MigrationInfoService {
return execute(object : Action<MigrationInfoService> {
override fun execute(session: Session): MigrationInfoService? {
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)
Expand All @@ -128,8 +139,8 @@ class CassandraMigration : CassandraMigrationConfiguration {
val validationError = execute(object : Action<String?> {
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)
val schemaVersionDAO = SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.table)
val validate = Validate(migrationResolver, configs.target, schemaVersionDAO, true, false)
return validate.run()
}
})
Expand All @@ -143,8 +154,14 @@ class CassandraMigration : CassandraMigrationConfiguration {
* Baselines an existing database, excluding all migrations up to and including baselineVersion.
*/
fun baseline() {
// TODO: Create the Cassandra migration implementation, refer to existing PR: https://github.com/Contrast-Security-OSS/cassandra-migration/pull/17
throw NotImplementedException()
execute(object : Action<Unit> {
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()
}
})
}

/**
Expand Down Expand Up @@ -197,7 +214,7 @@ class CassandraMigration : CassandraMigrationConfiguration {
else
throw CassandraMigrationException("Keyspace: " + keyspace.name + " does not exist.")

result = action.execute(session)!!
result = action.execute(session)
} finally {
if (null != session && !session.isClosed)
try {
Expand Down Expand Up @@ -259,7 +276,7 @@ class CassandraMigration : CassandraMigrationConfiguration {
* @param session The Cassandra session connection to use to execute the migration.
* @return The action result.
*/
fun execute(session: Session): T?
fun execute(session: Session): T

}

Expand Down
19 changes: 9 additions & 10 deletions src/main/java/com/builtamont/cassandra/migration/CommandLine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import com.builtamont.cassandra.migration.internal.util.logging.Log
import com.builtamont.cassandra.migration.internal.util.logging.LogFactory
import com.builtamont.cassandra.migration.internal.util.logging.console.ConsoleLog
import com.builtamont.cassandra.migration.internal.util.logging.console.ConsoleLogCreator
import java.util.*

/**
* Cassandra migration command line runner.
Expand All @@ -40,6 +39,11 @@ object CommandLine {
*/
val VALIDATE = "validate"

/**
* Command to trigger baseline action.
*/
val BASELINE = "baseline"

/**
* Logging support.
*/
Expand Down Expand Up @@ -69,22 +73,16 @@ object CommandLine {
cm.migrate()
} else if (VALIDATE.equals(operation, ignoreCase = true)) {
cm.validate()
} else if (BASELINE.equals(operation, ignoreCase = true)) {
cm.baseline()
}
}

/**
* Get a list of applicable operations.
*/
private fun determineOperations(args: Array<String>): List<String> {
val operations = ArrayList<String>()

for (arg in args) {
if (!arg.startsWith("-")) {
operations.add(arg)
}
}

return operations
return args.filterNot { it.startsWith("-") }
}

/**
Expand Down Expand Up @@ -124,6 +122,7 @@ object CommandLine {
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 up to, 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")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* File : Baseline.kt
* 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.internal.command

import com.builtamont.cassandra.migration.api.CassandraMigrationException
import com.builtamont.cassandra.migration.api.MigrationVersion
import com.builtamont.cassandra.migration.api.resolver.MigrationResolver
import com.builtamont.cassandra.migration.internal.dbsupport.SchemaVersionDAO

/**
* Handles the baseline command.
*
* @param migrationResolver The Cassandra migration resolver.
* @param baselineVersion The baseline version of the migration.
* @param schemaVersionDAO The Cassandra migration schema version DAO.
* @param baselineDescription The baseline version description / comments.
* @param user The user to execute the migration as.
*/
class Baseline(
private val migrationResolver: MigrationResolver,
private val baselineVersion: MigrationVersion,
private val schemaVersionDAO: SchemaVersionDAO,
private val baselineDescription: String,
private val user: String
) {

/**
* Runs the migration baselining.
*
* @return The number of successfully applied migration baselining.
* @throws CassandraMigrationException when migration baselining failed for any reason.
*/
@Throws(CassandraMigrationException::class)
fun run() {
val baselineMigration = schemaVersionDAO.baselineMarker
if (schemaVersionDAO.hasAppliedMigrations()) {
val msg = "Unable to baseline metadata table ${schemaVersionDAO.tableName} as it already contains migrations"
throw CassandraMigrationException(msg)
}

if (schemaVersionDAO.hasBaselineMarker()) {
val isNotBaselineByVersion = !(baselineMigration.version?.equals(baselineVersion) ?: false)
val isNotBaselineByDescription = !baselineMigration.description.equals(baselineDescription)
if (isNotBaselineByVersion || isNotBaselineByDescription) {
val msg = "Unable to baseline metadata table ${schemaVersionDAO.tableName} with ($baselineVersion, $baselineDescription)" +
" as it has already been initialized with (${baselineMigration.version}, ${baselineMigration.description})"
throw CassandraMigrationException(msg)
}
} else {
if (baselineVersion.equals(MigrationVersion.fromVersion("0"))) {
val msg = "Unable to baseline metadata table ${schemaVersionDAO.tableName} with version 0 as this version was used for schema creation"
throw CassandraMigrationException(msg)
}
schemaVersionDAO.addBaselineMarker(baselineVersion, baselineDescription, user)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class Migrate(
* @return The migration success log message.
*/
fun successLogMsg(): String {
return "Successfully applied $count migration to keyspace $keyspaceName (execution time ${TimeFormat.format(executionTime)})"
return "Successfully applied $count migration(s) to keyspace $keyspaceName (execution time ${TimeFormat.format(executionTime)})"
}

when (count) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class Validate(
* @param executionTime The total time taken to perform this migration run (in ms).
*/
private fun logSummary(count: Int, executionTime: Long) {
LOG.info("Validated %d migrations (execution time %s)".format(count, TimeFormat.format(executionTime)))
val time = TimeFormat.format(executionTime)
LOG.info("Validated $count migrations (execution time $time)")
}

/**
Expand Down
Loading