From f5c3ebf163635c365bc9611d9ec69c2a5276410a Mon Sep 17 00:00:00 2001 From: bhandy Date: Tue, 10 Feb 2015 07:54:11 -0500 Subject: [PATCH] - Split the staging of the compiled artifacts and the packaging of the application. - Added a new zip-package task to distribute the application as a zip file. - Added a PlainTextAuthProviderFactory to create a PlainTextAuthProvider instance from a properties file containing the credentials for Cassandra. - Added SslOptionsBuilder for constructing SSL settings to connect to Cassandra. --- project/PillarBuild.scala | 24 +++++++-- .../pillar/PlainTextAuthProviderFactory.scala | 19 +++++++ .../pillar/SslOptionsBuilder.scala | 33 ++++++++++++ .../com/chrisomeara/pillar/cli/App.scala | 51 +++++++++++++++++-- 4 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 src/main/scala/com/chrisomeara/pillar/PlainTextAuthProviderFactory.scala create mode 100644 src/main/scala/com/chrisomeara/pillar/SslOptionsBuilder.scala diff --git a/project/PillarBuild.scala b/project/PillarBuild.scala index 69c7bec..e55b2dc 100644 --- a/project/PillarBuild.scala +++ b/project/PillarBuild.scala @@ -22,8 +22,8 @@ object PillarBuild extends Build { "org.scalatest" %% "scalatest" % "2.2.0" % "test" ) - val rhPackage = TaskKey[File]("rh-package", "Packages the application for Red Hat Package Manager") - val rhPackageTask = rhPackage <<= (sourceDirectory, target, assembly, version) map { + val stage = TaskKey[File]("stage", "Stages the content of the final package.") + val stageTask = stage <<= (sourceDirectory, target, assembly, version) map { (sourceDirectory: File, targetDirectory: File, archive: File, versionId: String) => val rootPath = new File(targetDirectory, "staged-package") val subdirectories = Map( @@ -47,6 +47,22 @@ object PillarBuild extends Build { resource => IO.copyFile(new File(resourcesDirectory, resource), new File(subdirectories("conf"), resource)) } + targetDirectory + } + + val zipPackage = TaskKey[File]("zip-package", "Packages the application as a zip file.") + val zipPackageTask = zipPackage <<= (target, version) map { + (targetDirectory: File, versionId: String) => + "zip -r %s/pillar-%s.zip %s/staged-package/".format(targetDirectory.getPath(), versionId, targetDirectory.getPath()).! + + val zip = file("%s/pillar-%s.zip".format(targetDirectory.getPath(), versionId)) + if (!zip.exists()) throw new RuntimeException("Zip packing failed.") + zip + } + + val rhPackage = TaskKey[File]("rh-package", "Packages the application for Red Hat Package Manager.") + val rhPackageTask = rhPackage <<= (target, version) map { + (targetDirectory: File, versionId: String) => val iterationId = try { sys.env("GO_PIPELINE_COUNTER") } catch { case e: NoSuchElementException => "DEV" } "fpm -f -s dir -t rpm --package %s -n pillar --version %s --iteration %s -a all --prefix /opt/pillar -C %s/staged-package/ .".format(targetDirectory.getPath, versionId, iterationId, targetDirectory.getPath).! @@ -70,7 +86,9 @@ object PillarBuild extends Build { licenses := Seq("MIT license" -> url("http://www.opensource.org/licenses/mit-license.php")), scalaVersion := "2.10.4", crossScalaVersions := Seq("2.10.4", "2.11.1"), - rhPackageTask + rhPackageTask, + stageTask, + zipPackageTask ).settings( publishTo := { val nexus = "https://oss.sonatype.org/" diff --git a/src/main/scala/com/chrisomeara/pillar/PlainTextAuthProviderFactory.scala b/src/main/scala/com/chrisomeara/pillar/PlainTextAuthProviderFactory.scala new file mode 100644 index 0000000..f7d4dd2 --- /dev/null +++ b/src/main/scala/com/chrisomeara/pillar/PlainTextAuthProviderFactory.scala @@ -0,0 +1,19 @@ +package com.chrisomeara.pillar + +import java.io.FileReader +import java.util.Properties + +import com.datastax.driver.core.PlainTextAuthProvider +import com.google.common.base.Strings + +object PlainTextAuthProviderFactory { + def fromProperties(properties: Properties): PlainTextAuthProvider = { + val username = properties.getProperty("username") + val password = properties.getProperty("password") + if (Strings.isNullOrEmpty(username) || Strings.isNullOrEmpty(password)) { + return null + } + + new PlainTextAuthProvider(username, password) + } +} diff --git a/src/main/scala/com/chrisomeara/pillar/SslOptionsBuilder.scala b/src/main/scala/com/chrisomeara/pillar/SslOptionsBuilder.scala new file mode 100644 index 0000000..e190f70 --- /dev/null +++ b/src/main/scala/com/chrisomeara/pillar/SslOptionsBuilder.scala @@ -0,0 +1,33 @@ +package com.chrisomeara.pillar + +import java.io.FileInputStream +import java.security.{SecureRandom, KeyStore} +import javax.net.ssl.{KeyManager, TrustManagerFactory, SSLContext} + +import com.datastax.driver.core.SSLOptions + +class SslOptionsBuilder { + var keystore: KeyStore = null + var trustManagerFactory: TrustManagerFactory = null + var sslContext: SSLContext = null + + def withKeyStore(storeFile: String, storePassword: String, storeType: String = "JKS") = { + keystore = KeyStore.getInstance(storeType) + keystore.load(new FileInputStream(storeFile), storePassword.toCharArray) + } + + def withTrustManager(trustManagerAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm) = { + trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm) + } + + def withSslContext(sslContextType: String = "SSL") = { + sslContext = SSLContext.getInstance(sslContextType) + } + + def build(): SSLOptions = { + trustManagerFactory.init(keystore) + sslContext.init(new Array[KeyManager](0), trustManagerFactory.getTrustManagers, new SecureRandom()) + + new SSLOptions(sslContext, SSLOptions.DEFAULT_SSL_CIPHER_SUITES); + } +} diff --git a/src/main/scala/com/chrisomeara/pillar/cli/App.scala b/src/main/scala/com/chrisomeara/pillar/cli/App.scala index a8fb17d..fdc9c37 100644 --- a/src/main/scala/com/chrisomeara/pillar/cli/App.scala +++ b/src/main/scala/com/chrisomeara/pillar/cli/App.scala @@ -1,9 +1,11 @@ package com.chrisomeara.pillar.cli -import java.io.File +import java.io.{FileReader, File} +import java.util.Properties -import com.chrisomeara.pillar.{ConfigurationException, PrintStreamReporter, Registry, Reporter} +import com.chrisomeara.pillar._ import com.datastax.driver.core.Cluster +import com.google.common.base.Strings import com.typesafe.config.{Config, ConfigFactory} object App { @@ -34,10 +36,36 @@ class App(reporter: Reporter) { val keyspace = getFromConfiguration(configuration, dataStoreName, environment, "cassandra-keyspace-name") val seedAddress = getFromConfiguration(configuration, dataStoreName, environment, "cassandra-seed-address") val port = Integer.valueOf(getFromConfiguration(configuration, dataStoreName, environment, "cassandra-port", Some(9042.toString))) - val builder = Cluster.builder().addContactPoint(seedAddress).withPort(port).build() + val properties = loadProperties(configuration, dataStoreName, environment) + + val clusterBuilder = Cluster.builder().addContactPoint(seedAddress).withPort(port) + if (properties != null) { + val authProvider = PlainTextAuthProviderFactory.fromProperties(properties) + if (authProvider != null) { + clusterBuilder.withAuthProvider(authProvider) + } + + val useSsl = properties.getProperty("useSsl") + if (!Strings.isNullOrEmpty(useSsl) && useSsl.toBoolean) { + val optionsBuilder = new SslOptionsBuilder + + val trustStoreFile = properties.getProperty("trust-store-path") + val trustStorePassword = properties.getProperty("trust-store-password") + if (!Strings.isNullOrEmpty(trustStoreFile) && !Strings.isNullOrEmpty(trustStorePassword)) { + optionsBuilder.withKeyStore(trustStoreFile, trustStorePassword) + } + + optionsBuilder.withSslContext() + optionsBuilder.withTrustManager() + + clusterBuilder.withSSL(optionsBuilder.build()) + } + } + + val cluster = clusterBuilder.build() val session = commandLineConfiguration.command match { - case Initialize => builder.connect() - case _ => builder.connect(keyspace) + case Initialize => cluster.connect() + case _ => cluster.connect(keyspace) } val command = Command(commandLineConfiguration.command, session, keyspace, commandLineConfiguration.timeStampOption, registry) @@ -54,4 +82,17 @@ class App(reporter: Reporter) { if (default.eq(None)) throw new ConfigurationException(s"$path not found in application configuration") default.get } + + private def loadProperties(configuration: Config, name: String, environment: String): Properties = { + val properties = new Properties() + val credentialsPath = getFromConfiguration(configuration, name, environment, "credentials-path") + + if (Strings.isNullOrEmpty(credentialsPath)) { + return null + } + + properties.load(new FileReader(credentialsPath)) + properties + } + } \ No newline at end of file