diff --git a/cassandra-migration-spring-boot-starter/pom.xml b/cassandra-migration-spring-boot-starter/pom.xml index 2b712c2..b0cdd23 100644 --- a/cassandra-migration-spring-boot-starter/pom.xml +++ b/cassandra-migration-spring-boot-starter/pom.xml @@ -61,6 +61,12 @@ ${spring.boot.version} provided + + org.springframework.boot + spring-boot-actuator + ${spring.boot.version} + provided + org.springframework.boot spring-boot-test-autoconfigure diff --git a/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationAutoConfiguration.java b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationAutoConfiguration.java index 9305ae1..8ed59ce 100644 --- a/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationAutoConfiguration.java +++ b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationAutoConfiguration.java @@ -1,15 +1,19 @@ package org.cognitor.cassandra.migration.spring; import com.datastax.driver.core.Cluster; +import org.cognitor.cassandra.migration.Database; import org.cognitor.cassandra.migration.MigrationRepository; import org.cognitor.cassandra.migration.collector.FailOnDuplicatesCollector; import org.cognitor.cassandra.migration.collector.IgnoreDuplicatesCollector; import org.cognitor.cassandra.migration.keyspace.KeyspaceDefinition; import org.cognitor.cassandra.migration.scanner.ScannerRegistry; +import org.cognitor.cassandra.migration.spring.health.MigrationStatus; import org.cognitor.cassandra.migration.spring.scanner.SpringBootLocationScanner; import org.cognitor.cassandra.migration.tasks.TaskChain; import org.cognitor.cassandra.migration.tasks.TaskChainBuilder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -28,36 +32,60 @@ @ConditionalOnClass(Cluster.class) public class CassandraMigrationAutoConfiguration { private final CassandraMigrationConfigurationProperties properties; + private final Cluster cluster; @Autowired - public CassandraMigrationAutoConfiguration(CassandraMigrationConfigurationProperties properties) { + public CassandraMigrationAutoConfiguration(CassandraMigrationConfigurationProperties properties, Cluster cluster) { this.properties = properties; + this.cluster = cluster; } - - @Bean + @Bean(initMethod = "execute") @ConditionalOnBean(Cluster.class) @ConditionalOnMissingBean(TaskChain.class) - public TaskChain migrationProcess(Cluster cluster) { + public TaskChain migrationProcess() { + if (properties.isEnabled()) { + return createMigrationTaskChain(); + } else { + return createEmptyTaskChain(); + } + } + + private TaskChain createEmptyTaskChain() { + return new TaskChain(); + } + + private TaskChain createMigrationTaskChain() { + return new TaskChainBuilder(cluster, + migrationConfiguration(), + migrationRepository()) + .buildTaskChain(migrationDatabase()); + } + + @Bean + org.cognitor.cassandra.migration.Configuration migrationConfiguration() { if (!properties.hasKeyspaceName()) { throw new IllegalStateException("Please specify ['cassandra.migration.keyspace-name'] in" + " order to migrate your database"); } KeyspaceDefinition keyspaceDefinition = new KeyspaceDefinition(properties.getKeyspaceName()) .with(properties.getReplicationStrategy()); - return new TaskChainBuilder(cluster, - new org.cognitor.cassandra.migration.Configuration(keyspaceDefinition) - .setChecksumValidation(properties.isChecksumValidation()) + return new org.cognitor.cassandra.migration.Configuration(keyspaceDefinition).setChecksumValidation(properties.isChecksumValidation()) .setValidateOnly(properties.isChecksumValidationOnly()) .setRecalculateChecksum(properties.isRecalculateChecksum()) .setRecalculateChecksumOnly(properties.isRecalculateChecksumOnly()) .setConsistencyLevel(properties.getConsistencyLevel()) - .setCreateKeyspace(properties.isCreateKeyspace()), - createRepository()) - .buildTaskChain(); + .setCreateKeyspace(properties.isCreateKeyspace()); + } + + @Bean + Database migrationDatabase(){ + return new Database(cluster, migrationConfiguration()); } - private MigrationRepository createRepository() { + @ConditionalOnMissingBean(MigrationRepository.class) + @Bean + MigrationRepository migrationRepository() { ScannerRegistry registry = new ScannerRegistry(); registry.register(ScannerRegistry.JAR_SCHEME, new SpringBootLocationScanner()); if (properties.getStrategy() == ScriptCollectorStrategy.FAIL_ON_DUPLICATES) { @@ -65,4 +93,10 @@ private MigrationRepository createRepository() { } return new MigrationRepository(properties.getScriptLocation(), new IgnoreDuplicatesCollector(), registry); } + + @ConditionalOnClass(HealthIndicator.class) + @Bean + MigrationStatus migrationStatus() { + return new MigrationStatus(migrationRepository(), migrationDatabase()); + } } diff --git a/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationProperties.java b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationProperties.java index e0b24e5..6151a4a 100644 --- a/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationProperties.java +++ b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationProperties.java @@ -17,6 +17,7 @@ */ @ConfigurationProperties(prefix = "cassandra.migration") public class CassandraMigrationConfigurationProperties { + private boolean enabled = true; private ScriptCollectorStrategy strategy = ScriptCollectorStrategy.FAIL_ON_DUPLICATES; private String scriptLocation = MigrationRepository.DEFAULT_SCRIPT_PATH; private String keyspaceName; @@ -29,6 +30,14 @@ public class CassandraMigrationConfigurationProperties { private boolean recalculateChecksum = false; private ConsistencyLevel consistencyLevel = ConsistencyLevel.QUORUM; + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + /** * @return The location of the migration scripts. Never null. */ diff --git a/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/annotation/AfterMigration.java b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/annotation/AfterMigration.java new file mode 100644 index 0000000..e77d058 --- /dev/null +++ b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/annotation/AfterMigration.java @@ -0,0 +1,14 @@ +package org.cognitor.cassandra.migration.spring.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.DependsOn; + +@DependsOn("migrationProcess") +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface AfterMigration { +} diff --git a/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/health/MigrationStatus.java b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/health/MigrationStatus.java new file mode 100644 index 0000000..9f4946d --- /dev/null +++ b/cassandra-migration-spring-boot-starter/src/main/java/org/cognitor/cassandra/migration/spring/health/MigrationStatus.java @@ -0,0 +1,42 @@ +package org.cognitor.cassandra.migration.spring.health; + +import org.cognitor.cassandra.migration.Database; +import org.cognitor.cassandra.migration.MigrationRepository; +import org.cognitor.cassandra.migration.spring.CassandraMigrationConfigurationProperties; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@EnableConfigurationProperties(CassandraMigrationConfigurationProperties.class) +public class MigrationStatus implements HealthIndicator { + + private final MigrationRepository migrationRepository; + private final Database migrationDatabase; + + public MigrationStatus(MigrationRepository migrationRepository, Database database) { + this.migrationRepository = migrationRepository; + this.migrationDatabase = database; + } + + @Override + public Health health() { + try { + int latestVersion = migrationRepository.getLatestVersion(); + final int dbVersion = migrationDatabase.getVersion(); + + if(latestVersion != dbVersion) { + return Health.unknown() + .withDetail("databaseVersion", dbVersion) + .withDetail("sourceVersion", latestVersion) + .build(); + } else { + return Health.up() + .withDetail("databaseVersion", dbVersion) + .withDetail("sourceVersion", latestVersion) + .build(); + } + } catch (Throwable ex) { + return Health.unknown().withException(ex).build(); + } + } +} diff --git a/cassandra-migration-spring-boot-starter/src/test/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationPropertiesTest.java b/cassandra-migration-spring-boot-starter/src/test/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationPropertiesTest.java index 36f4d8e..98b07b7 100644 --- a/cassandra-migration-spring-boot-starter/src/test/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationPropertiesTest.java +++ b/cassandra-migration-spring-boot-starter/src/test/java/org/cognitor/cassandra/migration/spring/CassandraMigrationConfigurationPropertiesTest.java @@ -2,7 +2,9 @@ import com.datastax.driver.core.ConsistencyLevel; import org.junit.Test; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.Is.is; @@ -30,7 +32,7 @@ public void shouldPopulatePropertiesWhenPropertiesFileGiven() { addEnvironment(context, "cassandra.migration.network-strategy.data-centers.boston=3"); addEnvironment(context, "cassandra.migration.network-strategy.data-centers.seattle=2"); addEnvironment(context, "cassandra.migration.network-strategy.data-centers.tokyo=2"); - context.register(CassandraMigrationAutoConfiguration.class); + context.register(PropertiesTestConfiguration.class); context.refresh(); CassandraMigrationConfigurationProperties properties = context.getBean(CassandraMigrationConfigurationProperties.class); @@ -49,7 +51,7 @@ public void shouldPopulatePropertiesWhenPropertiesFileGiven() { public void shouldReturnDefaultValuesWhenNoOptionalPropertiesGiven() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(CassandraMigrationAutoConfiguration.class); + context.register(PropertiesTestConfiguration.class); context.refresh(); CassandraMigrationConfigurationProperties properties = context.getBean(CassandraMigrationConfigurationProperties.class); @@ -57,4 +59,15 @@ public void shouldReturnDefaultValuesWhenNoOptionalPropertiesGiven() { assertThat(properties.getScriptLocation(), is(equalTo("cassandra/migration"))); assertThat(properties.getStrategy(), is(equalTo(ScriptCollectorStrategy.FAIL_ON_DUPLICATES))); } -} \ No newline at end of file + + @Configuration + @EnableConfigurationProperties({CassandraMigrationConfigurationProperties.class}) + static class PropertiesTestConfiguration { + private final CassandraMigrationConfigurationProperties properties; + + public PropertiesTestConfiguration(CassandraMigrationConfigurationProperties properties) { + this.properties = properties; + } + } + +} diff --git a/cassandra-migration/src/main/java/org/cognitor/cassandra/migration/tasks/TaskChainBuilder.java b/cassandra-migration/src/main/java/org/cognitor/cassandra/migration/tasks/TaskChainBuilder.java index 8cc4870..cda1f7a 100644 --- a/cassandra-migration/src/main/java/org/cognitor/cassandra/migration/tasks/TaskChainBuilder.java +++ b/cassandra-migration/src/main/java/org/cognitor/cassandra/migration/tasks/TaskChainBuilder.java @@ -32,6 +32,10 @@ public TaskChainBuilder(Cluster cluster, Configuration configuration, MigrationR public TaskChain buildTaskChain() { Database database = new Database(cluster, configuration); + return buildTaskChain(database); + } + + public TaskChain buildTaskChain(Database database) { TaskChain chain = new TaskChain(); if (configuration.isRecalculateChecksumOnly()) { return chain.addTask(new RecalculateChecksumTask(database, migrationRepository));