Skip to content

Commit

Permalink
Merge pull request sky-uk#31 from sky-uk/add-authentication
Browse files Browse the repository at this point in the history
Add authentication
  • Loading branch information
chbatey committed Aug 2, 2016
2 parents e83053a + a076f86 commit ee84fc3
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 40 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ The migrator will look for a `bootstrap.cql` file for setting up the keyspace.
$ java -Dhosts=localhost,192.168.1.1 -Dkeyspace=my_keyspace -Ddirectories=cql-common,cql-local -jar cqlmigrate.jar
```

Specify credentials, if required, using `-Dusername=<username>` and `-Dpassword=<password>`.

## What it does

1. Checks all nodes are up and their schemas are in agreement.
Expand Down
14 changes: 14 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
id 'maven-publish'
id 'com.jfrog.bintray' version '1.6'
id 'pl.allegro.tech.build.axion-release' version '1.3.4'
id 'com.github.johnrengelman.shadow' version '1.2.3'
}

scmVersion {
Expand All @@ -25,6 +26,11 @@ if (!project.hasProperty('openSourceRepoPass')) {
ext.openSourceRepoPass = 'dummy'
}

clean.doFirst {
// Remove temporary directory for embedded Cassandra
delete "${projectDir}/target"
}

bintray {
user = project.property('openSourceRepoUser')
key = project.property('openSourceRepoPass')
Expand Down Expand Up @@ -77,6 +83,8 @@ dependencies {
testCompile 'org.assertj:assertj-core:2.0.0'
testCompile 'org.hamcrest:hamcrest-all:1.3'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.powermock:powermock-api-mockito:1.6.5'
testCompile 'org.powermock:powermock-module-junit4:1.6.5'

testCompile 'org.scassandra:java-client:1.0.3'

Expand Down Expand Up @@ -113,6 +121,12 @@ task testJar (type: Jar) {
}
}

jar {
manifest {
attributes "Main-Class": "uk.sky.cqlmigrate.CqlMigratorImpl"
}
}

// Functional test is to run the migrateSchema on a jar containing embedded CQL files and have it return successfully.
task functional (dependsOn: ['testJar']) << {
exec {
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/uk/sky/cqlmigrate/CassandraClusterFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package uk.sky.cqlmigrate;

import com.datastax.driver.core.Cluster;

public class CassandraClusterFactory {

/**
* Creates an instance of cassandra {@link Cluster} based on the provided configuration
*
* @return a configured Cluster
*/
public static Cluster createCluster(String[] hosts, int port, String username, String password) {
Cluster.Builder builder = Cluster.builder()
.addContactPoints(hosts)
.withPort(port);

if (username != null && password != null) {
builder = builder.withCredentials(username, password);
}

return builder.build();
}
}
12 changes: 10 additions & 2 deletions src/main/java/uk/sky/cqlmigrate/CqlMigrator.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public interface CqlMigrator {
*
* @param hosts Comma separated list of cassandra hosts
* @param port Native transport port for the above cassandra nodes
* @param username Username for Cassandra's internal authentication using PasswordAuthenticator
* (if using AllowAllAuthenticator, can be set to any value)
* @param password Password for Cassandra's internal authentication using PasswordAuthenticator
* (if using AllowAllAuthenticator, can be set to any value)
* @param keyspace Keyspace name for which the schema migration needs to be applied
* @param directories Comma separated list of directory paths containing the cql statements for the schema change
* @throws ClusterUnhealthyException if any nodes are down or the schema is not in agreement before running migration
Expand All @@ -35,7 +39,7 @@ public interface CqlMigrator {
* @throws IllegalStateException if cql file has changed after migration has been run
* @throws com.datastax.driver.core.exceptions.DriverException if any of the migration queries fails
*/
void migrate(String[] hosts, int port, String keyspace, Collection<Path> directories);
void migrate(String[] hosts, int port, String username, String password, String keyspace, Collection<Path> directories);

/**
* If all nodes are up and a lock can be acquired this runs migration
Expand Down Expand Up @@ -63,10 +67,14 @@ public interface CqlMigrator {
*
* @param hosts Comma separated list of cassandra hosts
* @param port Native transport port for the above cassandra nodes
* @param username Username for Cassandra's internal authentication using PasswordAuthenticator
* (if using AllowAllAuthenticator, can be set to any value)
* @param password Password for Cassandra's internal authentication using PasswordAuthenticator
* (if using AllowAllAuthenticator, can be set to any value)
* @param keyspace Keyspace name for which the schema migration needs to be applied
* @throws com.datastax.driver.core.exceptions.DriverException if query fails
*/
void clean(String[] hosts, int port, String keyspace);
void clean(String[] hosts, int port, String username, String password, String keyspace);

/**
* Drops keyspace if it exists
Expand Down
19 changes: 7 additions & 12 deletions src/main/java/uk/sky/cqlmigrate/CqlMigratorImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public static void main(String[] args) {
String keyspace = System.getProperty("keyspace");
String directoriesProperty = System.getProperty("directories");
String port = System.getProperty("port");
String username = System.getProperty("username");
String password = System.getProperty("password");

Preconditions.checkNotNull(hosts, "'hosts' property should be provided having value of a comma separated list of cassandra hosts");
Preconditions.checkNotNull(keyspace, "'keyspace' property should be provided having value of the cassandra keyspace");
Expand All @@ -48,14 +50,14 @@ public static void main(String[] args) {
.collect(Collectors.toList());

CqlMigratorFactory.create(CassandraLockConfig.builder().build())
.migrate(hosts.split(","), port == null ? 9042 : Integer.parseInt(port), keyspace, directories);
.migrate(hosts.split(","), port == null ? 9042 : Integer.parseInt(port), username, password, keyspace, directories);
}

/**
* {@inheritDoc}
*/
public void migrate(String[] hosts, int port, String keyspace, Collection<Path> directories) {
try (Cluster cluster = createCluster(hosts, port);
public void migrate(String[] hosts, int port, String username, String password, String keyspace, Collection<Path> directories) {
try (Cluster cluster = CassandraClusterFactory.createCluster(hosts, port, username, password);
Session session = cluster.connect()) {
this.migrate(session, keyspace, directories);
}
Expand Down Expand Up @@ -91,8 +93,8 @@ public void migrate(Session session, String keyspace, Collection<Path> directori
/**
* {@inheritDoc}
*/
public void clean(String[] hosts, int port, String keyspace) {
try (Cluster cluster = createCluster(hosts, port);
public void clean(String[] hosts, int port, String username, String password, String keyspace) {
try (Cluster cluster = CassandraClusterFactory.createCluster(hosts, port, username, password);
Session session = cluster.connect()) {
this.clean(session, keyspace);
}
Expand All @@ -107,11 +109,4 @@ public void clean(Session session, String keyspace) {
session.execute(clean);
LOGGER.info("Cleaned {}", keyspace);
}

private Cluster createCluster(String[] hosts, int port) {
return Cluster.builder()
.addContactPoints(hosts)
.withPort(port)
.build();
}
}
86 changes: 86 additions & 0 deletions src/test/java/uk/sky/cqlmigrate/CassandraClusterFactoryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package uk.sky.cqlmigrate;

import com.datastax.driver.core.Cluster;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Cluster.class})
public class CassandraClusterFactoryTest {

@Mock
private Cluster.Builder clusterBuilderMock;

@Mock
private Cluster clusterMock;


@Before
public void setUp() throws Exception {
mockStatic(Cluster.class);
when(Cluster.builder()).thenReturn(clusterBuilderMock);
when(clusterBuilderMock.addContactPoints(any(String[].class))).thenReturn(clusterBuilderMock);
when(clusterBuilderMock.withPort(anyInt())).thenReturn(clusterBuilderMock);
when(clusterBuilderMock.withCredentials(any(String.class), any(String.class))).thenReturn(clusterBuilderMock);
}

@Test
public void createClusterWithCredentials() throws Exception {
String expectedUser = "cassandra-user";
String expectedPassword = "cassandra-password";

//when
CassandraClusterFactory.createCluster(new String[]{"host1"}, 0, expectedUser, expectedPassword);

//then
verify(clusterBuilderMock).withCredentials(expectedUser, expectedPassword);
}

@Test
public void createClusterWithNullCredentials() throws Exception {
String expectedUser = null;
String expectedPassword = null;

//when
CassandraClusterFactory.createCluster(new String[]{"host1"}, 0, expectedUser, expectedPassword);

//then
verify(clusterBuilderMock, never()).withCredentials(expectedUser, expectedPassword);
}


@Test
public void createClusterWithNullPassword() throws Exception {
String expectedUser = "cassandra";
String expectedPassword = null;

//when
CassandraClusterFactory.createCluster(new String[]{"host1"}, 0, expectedUser, expectedPassword);

//then
verify(clusterBuilderMock, never()).withCredentials(expectedUser, expectedPassword);
}

@Test
public void createClusterWithNullUser() throws Exception {
String expectedUser = null;
String expectedPassword = "cassandra";

//when
CassandraClusterFactory.createCluster(new String[]{"host1"}, 0, expectedUser, expectedPassword);

//then
verify(clusterBuilderMock, never()).withCredentials(expectedUser, expectedPassword);
}
}
3 changes: 3 additions & 0 deletions src/test/java/uk/sky/cqlmigrate/ClusterHealthTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class ClusterHealthTest {

private static final int BINARY_PORT = PortScavenger.getFreePort();
private static final int ADMIN_PORT = PortScavenger.getFreePort();
private static String username = "cassandra";
private static String password = "cassandra";
private static final Collection<String> CASSANDRA_HOSTS = singletonList("localhost");

private static final Scassandra scassandra = ScassandraFactory.createServer(BINARY_PORT, ADMIN_PORT);
Expand All @@ -38,6 +40,7 @@ public void setUp() throws Exception {
cluster = Cluster.builder()
.addContactPoints(CASSANDRA_HOSTS.toArray(new String[CASSANDRA_HOSTS.size()]))
.withPort(BINARY_PORT)
.withCredentials(username, password)
.build();

cluster.connect();
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/uk/sky/cqlmigrate/CqlMigratorConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class CqlMigratorConfigTest {
public static CassandraLockConfig CASSANDRA_LOCK_CONFIG_BAD = null;

@Theory
public void shouldThrowIllegalArgumentExceptionWhenAnyPararmeterPassedIntoBuilderIsNul(CassandraLockConfig config, ConsistencyLevel readCL, ConsistencyLevel writeCL) {
public void shouldThrowIllegalArgumentExceptionWhenAnyParameterPassedIntoBuilderIsNull(CassandraLockConfig config, ConsistencyLevel readCL, ConsistencyLevel writeCL) {
Assume.assumeTrue(config == null || readCL == null || writeCL == null);

expectedException.expect(NullPointerException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public class CqlMigratorConsistencyLevelIntegrationTest {
public final ScassandraServerRule scassandra = new ScassandraServerRule(FREE_PORT, 1235);

private static final String[] CASSANDRA_HOSTS = {"localhost"};
private static String username = "cassandra";
private static String password = "cassandra";
public static final int FREE_PORT = PortScavenger.getFreePort();
private static final String CLIENT_ID = UUID.randomUUID().toString();
private static final String TEST_KEYSPACE = "cqlmigrate_test";
Expand All @@ -55,6 +57,7 @@ public void initialiseLockTable() throws ConfigurationException, IOException, TT
session = Cluster.builder()
.addContactPoints(CASSANDRA_HOSTS)
.withPort(FREE_PORT)
.withCredentials(username, password)
.build()
.connect();

Expand Down Expand Up @@ -93,7 +96,7 @@ public void shouldApplyCorrectConsistencyLevelsConfiguredForUnderlyingQueries()
Collection<Path> cqlPaths = asList(getResourcePath("cql_bootstrap"), getResourcePath("cql_consistency_level"));

//act
migrator.migrate(CASSANDRA_HOSTS, FREE_PORT, TEST_KEYSPACE, cqlPaths);
migrator.migrate(CASSANDRA_HOSTS, FREE_PORT, username, password, TEST_KEYSPACE, cqlPaths);

//assert

Expand Down
Loading

0 comments on commit ee84fc3

Please sign in to comment.