What we are trying to do
Use gradle to build and package our app. We are using gradle as our build tool as we did not like the Sbt way of doing things.
Deploy a single self contained running Spray application with all required runtimes dependency on Cloud Foundry as a java application.
If possible get off using our self written wrapper java tools to deploy and manipulate Cloud Foundry using the cloud foundry jar.
Background
We were trying out different gradle plugins to try and get our Spray app deployed. We had settled on oneJar as there was documentation of people using it with gradle to create and run a self contained file.
Onejar
The oneJar plugin changes the class loader to accept self contained single file that is a jar with jars inside it.
Messing around with class loading means you are going to have a bad time.
An idea from Spring Boot deployment
Chatting with some other devs I was also explaining we had problems deploying our Spring boot app, so were prevented from deploying onto our latest and greatest Cloud Foundry instance. We have hard gained experience on out how to deploy our apps on an older version of CF but it never started properly on the new CF. I was pointed to this by another developer as to how to deploy a Spring Boot application properly from the java buildpack documentation.
Cloud Foundry docs for Spring boot deployment
DistZip to the Rescue
This got me thinking of how Spring boot uses the distZip style deployment and could I leverage it to deploy my Spray app. Spring boot uses the distZip which comes from the application gradle plugin.
What distZip does
Creates a zip file with all the runtimes needed your application with the correct META-INF/MANIFEST.MF info and a start script to run it. Cloud Foundry is happy to deploy this.
You will need to set your main class to tell distZip what to run
mainClassName = "org.gradle.sample.Main"
Success
On Cloud Foundry the application needs to bind to a port provided on deployment time to accept connections, else CF will not let it succeed in starting and kill it.
Here is a code snippet of what it takes to bind the Spray to CF.
import com.typesafe.config._
....
val envConf: Config = ConfigFactory.systemEnvironment()
def serverPort = envConf.getInt("VCAP_APP_PORT")
....
IO(Http) ? Http.Bind(actorSystem.actorOf(Props(wire[YourActor]), "your-actor"), interface = "0.0.0.0", port = serverPort)
}
Using the Cloud Foundry Gradle Plugin
After getting a successful deployment using the command line tool(CLI) I was then able to use the Gradle CF plugin to configure and deploy. This makes it easier for Jenkins as we do not have to change our build slaves to deploy the CLI and change our chef scripts for build slaves.
Docs for CF gradle plugin
Setup snippet
<pre>
cloudfoundry {
target = "https://YOUR_INSTANCE.com"
space = "snapshot-YOURS"
file = file("./build/distributions/YOUR_DIST_ZIP.zip")
buildpack = "https://github.com/cloudfoundry/java-buildpack.git#v3.2"
application = "YOUR_APP_NAME-dev"
trustSelfSignedCerts = true // needed for our managed instance
memory = 1024
instances = 2
env = ["SPRING_PROFILES_ACTIVE": "A_PROFILE"] //we are using the spring boot style way of dealing with different properties files for different enviornments.
}</pre>
CF Commands
It is a bad idea to hold the credentials in your source control system. so you or your build slave should login and out using secured credentials.
- gradle cfLogin -PcfUsername=’your-user-name’ -PcfPassword=’your-password’ -PcfOrganization=’your-organisation’
With the above snippet the target, space, buildpack, file and application name you can just do a push without any additional commands.
2. gradle cfPush
Different Environments
Just override the needed elements to deploy to new environments with -PcfThingToOverride=’NEW_VALUE’
E.G.:
gradle cfPush -PcfTarget=’https://staging.your.company.com’ -PcfSpace=’stage-space’ -PcfInstances=’4′ -PcfApplication=’YOUR_APP-stage’ ….